diff --git a/docs/specification.rst b/docs/specification.rst index 3686ea8366a5db91d69fa0b085ca42885f5e177d..e2626078a1568fbe89232b32b10809099bdcbf9d 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -260,7 +260,7 @@ For the default HTTP transport, all API calls use a Content-Type of ``application/json``. In addition, all strings MUST be encoded as UTF-8. Clients are authenticated using opaque ``access_token`` strings (see -`Registration and Login`_ for details), passed as a querystring parameter on +`Registration and Login`_ for details), passed as a query string parameter on all requests. .. TODO @@ -375,1291 +375,1292 @@ When the client first logs in, they will need to initially synchronise with their home server. This is achieved via the |initialSync|_ API. This API also returns an ``end`` token which can be used with the event stream. -Rooms -===== - -Creation --------- -.. TODO kegan - - TODO: Key for invite these users? - -To create a room, a client has to use the |createRoom|_ API. There are various -options which can be set when creating a room: - -``visibility`` - Type: - String - Optional: - Yes - Value: - Either ``public`` or ``private``. - Description: - A ``public`` visibility indicates that the room will be shown in the public - room list. A ``private`` visibility will hide the room from the public room - list. Rooms default to ``public`` visibility if this key is not included. -``room_alias_name`` - Type: - String - Optional: - Yes - Value: - The room alias localpart. - Description: - If this is included, a room alias will be created and mapped to the newly - created room. The alias will belong on the same home server which created - the room, e.g. ``!qadnasoi:domain.com >>> #room_alias_name:domain.com`` +Registration and login +====================== -``name`` - Type: - String - Optional: - Yes - Value: - The ``name`` value for the ``m.room.name`` state event. - Description: - If this is included, an ``m.room.name`` event will be sent into the room to - indicate the name of the room. See `Room Events`_ for more information on - ``m.room.name``. +Clients must register with a home server in order to use Matrix. After +registering, the client will be given an access token which must be used in ALL +requests to that home server as a query parameter 'access_token'. -``topic`` - Type: - String - Optional: - Yes - Value: - The ``topic`` value for the ``m.room.topic`` state event. - Description: - If this is included, an ``m.room.topic`` event will be sent into the room - to indicate the topic for the room. See `Room Events`_ for more information - on ``m.room.topic``. +If the client has already registered, they need to be able to login to their +account. The home server may provide many different ways of logging in, such as +user/password auth, login via a social network (OAuth2), login by confirming a +token sent to their email address, etc. This specification does not define how +home servers should authorise their users who want to login to their existing +accounts, but instead defines the standard interface which implementations +should follow so that ANY client can login to ANY home server. Clients login +using the |login|_ API. Clients register using the |register|_ API. +Registration follows the same procedure as login, but the path requests are +sent to are different. -``invite`` - Type: - List - Optional: - Yes - Value: - A list of user ids to invite. - Description: - This will tell the server to invite everyone in the list to the newly - created room. +The registration/login process breaks down into the following: + 1. Determine the requirements for logging in. + 2. Submit the login stage credentials. + 3. Get credentials or be told the next stage in the login process and repeat + step 2. + +As each home server may have different ways of logging in, the client needs to +know how they should login. All distinct login stages MUST have a corresponding +``type``. A ``type`` is a namespaced string which details the mechanism for +logging in. -Example:: +A client may be able to login via multiple valid login flows, and should choose +a single flow when logging in. A flow is a series of login stages. The home +server MUST respond with all the valid login flows when requested:: + The client can login via 3 paths: 1a and 1b, 2a and 2b, or 3. The client should + select one of these paths. + { - "visibility": "public", - "room_alias_name": "the pub", - "name": "The Grand Duke Pub", - "topic": "All about happy hour" + "flows": [ + { + "type": "<login type1a>", + "stages": [ "<login type 1a>", "<login type 1b>" ] + }, + { + "type": "<login type2a>", + "stages": [ "<login type 2a>", "<login type 2b>" ] + }, + { + "type": "<login type3>" + } + ] } -The home server will create a ``m.room.create`` event when the room is created, -which serves as the root of the PDU graph for this room. This event also has a -``creator`` key which contains the user ID of the room creator. It will also -generate several other events in order to manage permissions in this room. This -includes: +After the login is completed, the client's fully-qualified user ID and a new +access token MUST be returned:: - - ``m.room.power_levels`` : Sets the power levels of users. - - ``m.room.join_rules`` : Whether the room is "invite-only" or not. - - ``m.room.add_state_level``: The power level required in order to add new - state to the room (as opposed to updating exisiting state) - - ``m.room.send_event_level`` : The power level required in order to send a - message in this room. - - ``m.room.ops_level`` : The power level required in order to kick or ban a - user from the room. + { + "user_id": "@user:matrix.org", + "access_token": "abcdef0123456789" + } -See `Room Events`_ for more information on these events. +The ``user_id`` key is particularly useful if the home server wishes to support +localpart entry of usernames (e.g. "user" rather than "@user:matrix.org"), as +the client may not be able to determine its ``user_id`` in this case. -Modifying aliases ------------------ -.. NOTE:: - This section is a work in progress. +If a login has multiple requests, the home server may wish to create a session. +If a home server responds with a 'session' key to a request, clients MUST +submit it in subsequent requests until the login is completed:: -.. TODO kegan - - path to edit aliases - - PUT /directory/room/<room alias> { room_id : foo } - - GET /directory/room/<room alias> { room_id : foo, servers: [a.com, b.com] } - - format when retrieving list of aliases. NOT complete list. - - format for adding/removing aliases. + { + "session": "<session id>" + } -Permissions ------------ -.. NOTE:: - This section is a work in progress. +This specification defines the following login types: + - ``m.login.password`` + - ``m.login.oauth2`` + - ``m.login.email.code`` + - ``m.login.email.url`` + - ``m.login.email.identity`` -.. TODO kegan - - TODO: What is a power level? How do they work? Defaults / required levels for X. How do they change - as people join and leave rooms? What do you do if you get a clash? Examples. - - TODO: List all actions which use power levels (sending msgs, inviting users, banning people, etc...) - - TODO: Room config - what is the event and what are the keys/values and explanations for them. - Link through to respective sections where necessary. How does this tie in with permissions, e.g. - give example of creating a read-only room. +Password-based +-------------- +:Type: + ``m.login.password`` +:Description: + Login is supported via a username and password. -Permissions for rooms are done via the concept of power levels - to do any -action in a room a user must have a suitable power level. +To respond to this type, reply with:: -Power levels for users are defined in ``m.room.power_levels``, where both a -default and specific users' power levels can be set. By default all users have -a power level of 0, other than the room creator whose power level defaults to -100. Power levels for users are tracked per-room even if the user is not -present in the room. + { + "type": "m.login.password", + "user": "<user_id or user localpart>", + "password": "<password>" + } -State events may contain a ``required_power_level`` key, which indicates the -minimum power a user must have before they can update that state key. The only -exception to this is when a user leaves a room. +The home server MUST respond with either new credentials, the next stage of the +login process, or a standard error response. -To perform certain actions there are additional power level requirements -defined in the following state events: +OAuth2-based +------------ +:Type: + ``m.login.oauth2`` +:Description: + Login is supported via OAuth2 URLs. This login consists of multiple requests. -- ``m.room.send_event_level`` defines the minimum level for sending non-state - events. Defaults to 50. -- ``m.room.add_state_level`` defines the minimum level for adding new state, - rather than updating existing state. Defaults to 50. -- ``m.room.ops_level`` defines the minimum levels to ban and kick other users. - This defaults to a kick and ban levels of 50 each. +To respond to this type, reply with:: + { + "type": "m.login.oauth2", + "user": "<user_id or user localpart>" + } -Joining rooms -------------- -.. TODO kegan - - TODO: What does the home server have to do to join a user to a room? +The server MUST respond with:: -Users need to join a room in order to send and receive events in that room. A -user can join a room by making a request to |/join/<room_alias_or_id>|_ with:: + { + "uri": <Authorization Request URI OR service selection URI> + } - {} +The home server acts as a 'confidential' client for the purposes of OAuth2. If +the uri is a ``sevice selection URI``, it MUST point to a webpage which prompts +the user to choose which service to authorize with. On selection of a service, +this MUST link through to an ``Authorization Request URI``. If there is only 1 +service which the home server accepts when logging in, this indirection can be +skipped and the "uri" key can be the ``Authorization Request URI``. -Alternatively, a user can make a request to |/rooms/<room_id>/join|_ with the -same request content. This is only provided for symmetry with the other -membership APIs: ``/rooms/<room id>/invite`` and ``/rooms/<room id>/leave``. If -a room alias was specified, it will be automatically resolved to a room ID, -which will then be joined. The room ID that was joined will be returned in -response:: +The client then visits the ``Authorization Request URI``, which then shows the +OAuth2 Allow/Deny prompt. Hitting 'Allow' returns the ``redirect URI`` with the +auth code. Home servers can choose any path for the ``redirect URI``. The +client should visit the ``redirect URI``, which will then finish the OAuth2 +login process, granting the home server an access token for the chosen service. +When the home server gets this access token, it verifies that the cilent has +authorised with the 3rd party, and can now complete the login. The OAuth2 +``redirect URI`` (with auth code) MUST respond with either new credentials, the +next stage of the login process, or a standard error response. + +For example, if a home server accepts OAuth2 from Google, it would return the +Authorization Request URI for Google:: { - "room_id": "!roomid:domain" + "uri": "https://accounts.google.com/o/oauth2/auth?response_type=code& + client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos" } -The membership state for the joining user can also be modified directly to be -``join`` by sending the following request to -``/rooms/<room id>/state/m.room.member/<url encoded user id>``:: +The client then visits this URI and authorizes the home server. The client then +visits the REDIRECT_URI with the auth code= query parameter which returns:: { - "membership": "join" + "user_id": "@user:matrix.org", + "access_token": "0123456789abcdef" } -See the `Room events`_ section for more information on ``m.room.member``. - -After the user has joined a room, they will receive subsequent events in that -room. This room will now appear as an entry in the |initialSync|_ API. - -Some rooms enforce that a user is *invited* to a room before they can join that -room. Other rooms will allow anyone to join the room even if they have not -received an invite. - -Inviting users --------------- -.. TODO kegan - - Can invite users to a room if the room config key TODO is set to TODO. Must have required power level. - - Outline invite join dance. What is it? Why is it required? How does it work? - - What does the home server have to do? - - TODO: In what circumstances will direct member editing NOT be equivalent to ``/invite``? +Email-based (code) +------------------ +:Type: + ``m.login.email.code`` +:Description: + Login is supported by typing in a code which is sent in an email. This login + consists of multiple requests. -The purpose of inviting users to a room is to notify them that the room exists -so they can choose to become a member of that room. Some rooms require that all -users who join a room are previously invited to it (an "invite-only" room). -Whether a given room is an "invite-only" room is determined by the room config -key ``TODO``. It can have one of the following values: +To respond to this type, reply with:: - - TODO Room config invite only value explanation - - TODO Room config free-to-join value explanation + { + "type": "m.login.email.code", + "user": "<user_id or user localpart>", + "email": "<email address>" + } -Only users who have a membership state of ``join`` in a room can invite new -users to said room. The person being invited must not be in the ``join`` state -in the room. The fully-qualified user ID must be specified when inviting a -user, as the user may reside on a different home server. To invite a user, send -the following request to |/rooms/<room_id>/invite|_, which will manage the -entire invitation process:: +After validating the email address, the home server MUST send an email +containing an authentication code and return:: { - "user_id": "<user id to invite>" + "type": "m.login.email.code", + "session": "<session id>" } -Alternatively, the membership state for this user in this room can be modified -directly by sending the following request to -``/rooms/<room id>/state/m.room.member/<url encoded user id>``:: +The second request in this login stage involves sending this authentication +code:: { - "membership": "invite" + "type": "m.login.email.code", + "session": "<session id>", + "code": "<code in email sent>" } -See the `Room events`_ section for more information on ``m.room.member``. +The home server MUST respond to this with either new credentials, the next +stage of the login process, or a standard error response. -Leaving rooms -------------- -.. TODO kegan - - TODO: Grace period before deletion? - - TODO: Under what conditions should a room NOT be purged? +Email-based (url) +----------------- +:Type: + ``m.login.email.url`` +:Description: + Login is supported by clicking on a URL in an email. This login consists of + multiple requests. +To respond to this type, reply with:: -A user can leave a room to stop receiving events for that room. A user must -have joined the room before they are eligible to leave the room. If the room is -an "invite-only" room, they will need to be re-invited before they can re-join -the room. To leave a room, a request should be made to -|/rooms/<room_id>/leave|_ with:: + { + "type": "m.login.email.url", + "user": "<user_id or user localpart>", + "email": "<email address>" + } - {} +After validating the email address, the home server MUST send an email +containing an authentication URL and return:: -Alternatively, the membership state for this user in this room can be modified -directly by sending the following request to -``/rooms/<room id>/state/m.room.member/<url encoded user id>``:: + { + "type": "m.login.email.url", + "session": "<session id>" + } + +The email contains a URL which must be clicked. After it has been clicked, the +client should perform another request:: { - "membership": "leave" + "type": "m.login.email.url", + "session": "<session id>" } -See the `Room events`_ section for more information on ``m.room.member``. +The home server MUST respond to this with either new credentials, the next +stage of the login process, or a standard error response. -Once a user has left a room, that room will no longer appear on the -|initialSync|_ API. Be aware that leaving a room is not equivalent to have -never been in that room. A user who has previously left a room still maintains -some residual state in that room. Their membership state will be marked as -``leave``. This contrasts with a user who has *never been invited or joined to -that room* who will not have any membership state for that room. +A common client implementation will be to periodically poll until the link is +clicked. If the link has not been visited yet, a standard error response with +an errcode of ``M_LOGIN_EMAIL_URL_NOT_YET`` should be returned. -If all members in a room leave, that room becomes eligible for deletion. -Banning users in a room ------------------------ -A user may decide to ban another user in a room. 'Banning' forces the target -user to leave the room and prevents them from re-joining the room. A banned -user will not be treated as a joined user, and so will not be able to send or -receive events in the room. In order to ban someone, the user performing the -ban MUST have the required power level. To ban a user, a request should be made -to |/rooms/<room_id>/ban|_ with:: +Email-based (identity server) +----------------------------- +:Type: + ``m.login.email.identity`` +:Description: + Login is supported by authorising an email address with an identity server. - { - "user_id": "<user id to ban" - "reason": "string: <reason for the ban>" - } - -Banning a user adjusts the banned member's membership state to ``ban`` and -adjusts the power level of this event to a level higher than the banned person. -Like with other membership changes, a user can directly adjust the target -member's state, by making a request to -``/rooms/<room id>/state/m.room.member/<user id>``:: +Prior to submitting this, the client should authenticate with an identity +server. After authenticating, the session information should be submitted to +the home server. + +To respond to this type, reply with:: { - "membership": "ban" + "type": "m.login.email.identity", + "threepidCreds": [ + { + "sid": "<identity server session id>", + "clientSecret": "<identity server client secret>", + "idServer": "<url of identity server authed with, e.g. 'matrix.org:8090'>" + } + ] } -Events in a room ----------------- -Room events can be split into two categories: - -:State Events: - These are events which replace events that came before it, depending on a set - of unique keys. These keys are the event ``type`` and a ``state_key``. - Events with the same set of keys will be overwritten. Typically, state events - are used to store state, hence their name. - -:Non-state events: - These are events which cannot be overwritten after sending. The list of - events continues to grow as more events are sent. As this list grows, it - becomes necessary to provide a mechanism for navigating this list. Pagination - APIs are used to view the list of historical non-state events. Typically, - non-state events are used to send messages. -This specification outlines several events, all with the event type prefix -``m.``. However, applications may wish to add their own type of event, and this -can be achieved using the REST API detailed in the following sections. If new -events are added, the event ``type`` key SHOULD follow the Java package naming -convention, e.g. ``com.example.myapp.event``. This ensures event types are -suitably namespaced for each application and reduces the risk of clashes. -State events ------------- -State events can be sent by ``PUT`` ing to -|/rooms/<room_id>/state/<event_type>/<state_key>|_. These events will be -overwritten if ``<room id>``, ``<event type>`` and ``<state key>`` all match. -If the state event has no ``state_key``, it can be omitted from the path. These -requests **cannot use transaction IDs** like other ``PUT`` paths because they -cannot be differentiated from the ``state_key``. Furthermore, ``POST`` is -unsupported on state paths. Valid requests look like:: +N-Factor Authentication +----------------------- +Multiple login stages can be combined to create N-factor authentication during +login. - PUT /rooms/!roomid:domain/state/m.example.event - { "key" : "without a state key" } +This can be achieved by responding with the ``next`` login type on completion +of a previous login stage:: - PUT /rooms/!roomid:domain/state/m.another.example.event/foo - { "key" : "with 'foo' as the state key" } + { + "next": "<next login type>" + } -In contrast, these requests are invalid:: +If a home server implements N-factor authentication, it MUST respond with all +``stages`` when initially queried for their login requirements:: - POST /rooms/!roomid:domain/state/m.example.event/ - { "key" : "cannot use POST here" } + { + "type": "<1st login type>", + "stages": [ <1st login type>, <2nd login type>, ... , <Nth login type> ] + } - PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11 - { "key" : "txnIds are not supported" } +This can be represented conceptually as:: -Care should be taken to avoid setting the wrong ``state key``:: + _______________________ + | Login Stage 1 | + | type: "<login type1>" | + | ___________________ | + | |_Request_1_________| | <-- Returns "session" key which is used throughout. + | ___________________ | + | |_Request_2_________| | <-- Returns a "next" value of "login type2" + |_______________________| + | + | + _________V_____________ + | Login Stage 2 | + | type: "<login type2>" | + | ___________________ | + | |_Request_1_________| | + | ___________________ | + | |_Request_2_________| | + | ___________________ | + | |_Request_3_________| | <-- Returns a "next" value of "login type3" + |_______________________| + | + | + _________V_____________ + | Login Stage 3 | + | type: "<login type3>" | + | ___________________ | + | |_Request_1_________| | <-- Returns user credentials + |_______________________| - PUT /rooms/!roomid:domain/state/m.another.example.event/11 - { "key" : "with '11' as the state key, but was probably intended to be a txnId" } +Fallback +-------- +Clients cannot be expected to be able to know how to process every single login +type. If a client determines it does not know how to handle a given login type, +it should request a login fallback page:: -The ``state_key`` is often used to store state about individual users, by using -the user ID as the ``state_key`` value. For example:: + GET matrix/client/api/v1/login/fallback - PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Adomain.com - { "animal" : "cat", "reason": "fluffy" } +This MUST return an HTML page which can perform the entire login process. -In some cases, there may be no need for a ``state_key``, so it can be omitted:: - PUT /rooms/!roomid:domain/state/m.room.bgd.color - { "color": "red", "hex": "#ff0000" } +Rooms +===== -See `Room Events`_ for the ``m.`` event specification. +Creation +-------- +.. TODO kegan + - TODO: Key for invite these users? + +To create a room, a client has to use the |createRoom|_ API. There are various +options which can be set when creating a room: -Non-state events ----------------- -Non-state events can be sent by sending a request to -|/rooms/<room_id>/send/<event_type>|_. These requests *can* use transaction -IDs and ``PUT``/``POST`` methods. Non-state events allow access to historical -events and pagination, making it best suited for sending messages. For -example:: +``visibility`` + Type: + String + Optional: + Yes + Value: + Either ``public`` or ``private``. + Description: + A ``public`` visibility indicates that the room will be shown in the public + room list. A ``private`` visibility will hide the room from the public room + list. Rooms default to ``public`` visibility if this key is not included. - POST /rooms/!roomid:domain/send/m.custom.example.message - { "text": "Hello world!" } +``room_alias_name`` + Type: + String + Optional: + Yes + Value: + The room alias localpart. + Description: + If this is included, a room alias will be created and mapped to the newly + created room. The alias will belong on the same home server which created + the room, e.g. ``!qadnasoi:domain.com >>> #room_alias_name:domain.com`` - PUT /rooms/!roomid:domain/send/m.custom.example.message/11 - { "text": "Goodbye world!" } +``name`` + Type: + String + Optional: + Yes + Value: + The ``name`` value for the ``m.room.name`` state event. + Description: + If this is included, an ``m.room.name`` event will be sent into the room to + indicate the name of the room. See `Room Events`_ for more information on + ``m.room.name``. -See `Room Events`_ for the ``m.`` event specification. +``topic`` + Type: + String + Optional: + Yes + Value: + The ``topic`` value for the ``m.room.topic`` state event. + Description: + If this is included, an ``m.room.topic`` event will be sent into the room + to indicate the topic for the room. See `Room Events`_ for more information + on ``m.room.topic``. -Syncing rooms -------------- +``invite`` + Type: + List + Optional: + Yes + Value: + A list of user ids to invite. + Description: + This will tell the server to invite everyone in the list to the newly + created room. + +Example:: + + { + "visibility": "public", + "room_alias_name": "the pub", + "name": "The Grand Duke Pub", + "topic": "All about happy hour" + } + +The home server will create a ``m.room.create`` event when the room is created, +which serves as the root of the PDU graph for this room. This event also has a +``creator`` key which contains the user ID of the room creator. It will also +generate several other events in order to manage permissions in this room. This +includes: + + - ``m.room.power_levels`` : Sets the power levels of users. + - ``m.room.join_rules`` : Whether the room is "invite-only" or not. + - ``m.room.add_state_level``: The power level required in order to add new + state to the room (as opposed to updating exisiting state) + - ``m.room.send_event_level`` : The power level required in order to send a + message in this room. + - ``m.room.ops_level`` : The power level required in order to kick or ban a + user from the room. + +See `Room Events`_ for more information on these events. + +Modifying aliases +----------------- .. NOTE:: This section is a work in progress. -When a client logs in, they may have a list of rooms which they have already -joined. These rooms may also have a list of events associated with them. The -purpose of 'syncing' is to present the current room and event information in a -convenient, compact manner. The events returned are not limited to room events; -presence events will also be returned. There are two APIs provided: - - - |initialSync|_ : A global sync which will present room and event information - for all rooms the user has joined. +.. TODO kegan + - path to edit aliases + - PUT /directory/room/<room alias> { room_id : foo } + - GET /directory/room/<room alias> { room_id : foo, servers: [a.com, b.com] } + - format when retrieving list of aliases. NOT complete list. + - format for adding/removing aliases. - - |/rooms/<room_id>/initialSync|_ : A sync scoped to a single room. Presents - room and event information for this room only. +Permissions +----------- +.. NOTE:: + This section is a work in progress. .. TODO kegan - - TODO: JSON response format for both types - - TODO: when would you use global? when would you use scoped? + - TODO: What is a power level? How do they work? Defaults / required levels for X. How do they change + as people join and leave rooms? What do you do if you get a clash? Examples. + - TODO: List all actions which use power levels (sending msgs, inviting users, banning people, etc...) + - TODO: Room config - what is the event and what are the keys/values and explanations for them. + Link through to respective sections where necessary. How does this tie in with permissions, e.g. + give example of creating a read-only room. -Getting events for a room -------------------------- -There are several APIs provided to ``GET`` events for a room: +Permissions for rooms are done via the concept of power levels - to do any +action in a room a user must have a suitable power level. -``/rooms/<room id>/state/<event type>/<state key>`` - Description: - Get the state event identified. - Response format: - A JSON object representing the state event **content**. - Example: - ``/rooms/!room:domain.com/state/m.room.name`` returns ``{ "name": "Room name" }`` +Power levels for users are defined in ``m.room.power_levels``, where both a +default and specific users' power levels can be set. By default all users have +a power level of 0, other than the room creator whose power level defaults to +100. Power levels for users are tracked per-room even if the user is not +present in the room. -|/rooms/<room_id>/state|_ - Description: - Get all state events for a room. - Response format: - ``[ { state event }, { state event }, ... ]`` - Example: - TODO +State events may contain a ``required_power_level`` key, which indicates the +minimum power a user must have before they can update that state key. The only +exception to this is when a user leaves a room. +To perform certain actions there are additional power level requirements +defined in the following state events: -|/rooms/<room_id>/members|_ - Description: - Get all ``m.room.member`` state events. - Response format: - ``{ "start": "<token>", "end": "<token>", "chunk": [ { m.room.member event }, ... ] }`` - Example: - TODO +- ``m.room.send_event_level`` defines the minimum level for sending non-state + events. Defaults to 50. +- ``m.room.add_state_level`` defines the minimum level for adding new state, + rather than updating existing state. Defaults to 50. +- ``m.room.ops_level`` defines the minimum levels to ban and kick other users. + This defaults to a kick and ban levels of 50 each. -|/rooms/<room_id>/messages|_ - Description: - Get all ``m.room.message`` and ``m.room.member`` events. This API supports - pagination using ``from`` and ``to`` query parameters, coupled with the - ``start`` and ``end`` tokens from an |initialSync|_ API. - Response format: - ``{ "start": "<token>", "end": "<token>" }`` - Example: - TODO - -|/rooms/<room_id>/initialSync|_ - Description: - Get all relevant events for a room. This includes state events, paginated non-state - events and presence events. - Response format: - `` { TODO } `` - Example: - TODO +Joining rooms +------------- +.. TODO kegan + - TODO: What does the home server have to do to join a user to a room? -Room Events -=========== -.. NOTE:: - This section is a work in progress. +Users need to join a room in order to send and receive events in that room. A +user can join a room by making a request to |/join/<room_alias_or_id>|_ with:: -.. TODO dave? - - voip events? + {} -This specification outlines several standard event types, all of which are -prefixed with ``m.`` +Alternatively, a user can make a request to |/rooms/<room_id>/join|_ with the +same request content. This is only provided for symmetry with the other +membership APIs: ``/rooms/<room id>/invite`` and ``/rooms/<room id>/leave``. If +a room alias was specified, it will be automatically resolved to a room ID, +which will then be joined. The room ID that was joined will be returned in +response:: -``m.room.name`` - Summary: - Set the human-readable name for the room. - Type: - State event - JSON format: - ``{ "name" : "string" }`` - Example: - ``{ "name" : "My Room" }`` - Description: - A room has an opaque room ID which is not human-friendly to read. A room - alias is human-friendly, but not all rooms have room aliases. The room name - is a human-friendly string designed to be displayed to the end-user. The - room name is not *unique*, as multiple rooms can have the same room name - set. The room name can also be set when creating a room using |createRoom|_ - with the ``name`` key. + { + "room_id": "!roomid:domain" + } -``m.room.topic`` - Summary: - Set a topic for the room. - Type: - State event - JSON format: - ``{ "topic" : "string" }`` - Example: - ``{ "topic" : "Welcome to the real world." }`` - Description: - A topic is a short message detailing what is currently being discussed in - the room. It can also be used as a way to display extra information about - the room, which may not be suitable for the room name. The room topic can - also be set when creating a room using |createRoom|_ with the ``topic`` - key. +The membership state for the joining user can also be modified directly to be +``join`` by sending the following request to +``/rooms/<room id>/state/m.room.member/<url encoded user id>``:: -``m.room.member`` - Summary: - The current membership state of a user in the room. - Type: - State event - JSON format: - ``{ "membership" : "enum[ invite|join|leave|ban ]" }`` - Example: - ``{ "membership" : "join" }`` - Description: - Adjusts the membership state for a user in a room. It is preferable to use - the membership APIs (``/rooms/<room id>/invite`` etc) when performing - membership actions rather than adjusting the state directly as there are a - restricted set of valid transformations. For example, user A cannot force - user B to join a room, and trying to force this state change directly will - fail. See the `Rooms`_ section for how to use the membership APIs. + { + "membership": "join" + } -``m.room.create`` - Summary: - The first event in the room. - Type: - State event - JSON format: - ``{ "creator": "string"}`` - Example: - ``{ "creator": "@user:example.com" }`` - Description: - This is the first event in a room and cannot be changed. It acts as the - root of all other events. +See the `Room events`_ section for more information on ``m.room.member``. -``m.room.join_rules`` - Summary: - Descripes how/if people are allowed to join. - Type: - State event - JSON format: - ``{ "join_rule": "enum [ public|knock|invite|private ]" }`` - Example: - ``{ "join_rule": "public" }`` - Description: - TODO : Use docs/models/rooms.rst - -``m.room.power_levels`` - Summary: - Defines the power levels of users in the room. - Type: - State event - JSON format: - ``{ "<user_id>": <int>, ..., "default": <int>}`` - Example: - ``{ "@user:example.com": 5, "@user2:example.com": 10, "default": 0 }`` - Description: - If a user is in the list, then they have the associated power level. - Otherwise they have the default level. If not ``default`` key is supplied, - it is assumed to be 0. +After the user has joined a room, they will receive subsequent events in that +room. This room will now appear as an entry in the |initialSync|_ API. -``m.room.add_state_level`` - Summary: - Defines the minimum power level a user needs to add state. - Type: - State event - JSON format: - ``{ "level": <int> }`` - Example: - ``{ "level": 5 }`` - Description: - To add a new piece of state to the room a user must have the given power - level. This does not apply to updating current state, which is goverened - by the ``required_power_level`` event key. - -``m.room.send_event_level`` - Summary: - Defines the minimum power level a user needs to send an event. - Type: - State event - JSON format: - ``{ "level": <int> }`` - Example: - ``{ "level": 0 }`` - Description: - To send a new event into the room a user must have at least this power - level. This allows ops to make the room read only by increasing this level, - or muting individual users by lowering their power level below this - threshold. +Some rooms enforce that a user is *invited* to a room before they can join that +room. Other rooms will allow anyone to join the room even if they have not +received an invite. -``m.room.ops_levels`` - Summary: - Defines the minimum power levels that a user must have before they can - kick and/or ban other users. - Type: - State event - JSON format: - ``{ "ban_level": <int>, "kick_level": <int> }`` - Example: - ``{ "ban_level": 5, "kick_level": 5 }`` - Description: - This defines who can ban and/or kick people in the room. Most of the time - ``ban_level`` will be greater than or equal to ``kick_level`` since - banning is more severe than kicking. +Inviting users +-------------- +.. TODO kegan + - Can invite users to a room if the room config key TODO is set to TODO. Must have required power level. + - Outline invite join dance. What is it? Why is it required? How does it work? + - What does the home server have to do? + - TODO: In what circumstances will direct member editing NOT be equivalent to ``/invite``? -``m.room.aliases`` - Summary: - These state events are used to inform the room about what room aliases it - has. - Type: - State event - JSON format: - ``{ "aliases": ["string", ...] }`` - Example: - ``{ "aliases": ["#foo:example.com"] }`` - Description: - A server `may` inform the room that it has added or removed an alias for - the room. This is purely for informational purposes and may become stale. - Clients `should` check that the room alias is still valid before using it. - The ``state_key`` of the event is the homeserver which owns the room alias. +The purpose of inviting users to a room is to notify them that the room exists +so they can choose to become a member of that room. Some rooms require that all +users who join a room are previously invited to it (an "invite-only" room). +Whether a given room is an "invite-only" room is determined by the room config +key ``TODO``. It can have one of the following values: -``m.room.message`` - Summary: - A message. - Type: - Non-state event - JSON format: - ``{ "msgtype": "string" }`` - Example: - ``{ "msgtype": "m.text", "body": "Testing" }`` - Description: - This event is used when sending messages in a room. Messages are not - limited to be text. The ``msgtype`` key outlines the type of message, e.g. - text, audio, image, video, etc. Whilst not required, the ``body`` key - SHOULD be used with every kind of ``msgtype`` as a fallback mechanism when - a client cannot render the message. For more information on the types of - messages which can be sent, see `m.room.message msgtypes`_. + - TODO Room config invite only value explanation + - TODO Room config free-to-join value explanation -``m.room.message.feedback`` - Summary: - A receipt for a message. - Type: - Non-state event - JSON format: - ``{ "type": "enum [ delivered|read ]", "target_event_id": "string" }`` - Example: - ``{ "type": "delivered", "target_event_id": "e3b2icys" }`` - Description: - Feedback events are events sent to acknowledge a message in some way. There - are two supported acknowledgements: ``delivered`` (sent when the event has - been received) and ``read`` (sent when the event has been observed by the - end-user). The ``target_event_id`` should reference the ``m.room.message`` - event being acknowledged. +Only users who have a membership state of ``join`` in a room can invite new +users to said room. The person being invited must not be in the ``join`` state +in the room. The fully-qualified user ID must be specified when inviting a +user, as the user may reside on a different home server. To invite a user, send +the following request to |/rooms/<room_id>/invite|_, which will manage the +entire invitation process:: -m.room.message msgtypes ------------------------ -Each ``m.room.message`` MUST have a ``msgtype`` key which identifies the type -of message being sent. Each type has their own required and optional keys, as -outlined below: + { + "user_id": "<user id to invite>" + } -``m.text`` - Required keys: - - ``body`` : "string" - The body of the message. - Optional keys: - None. - Example: - ``{ "msgtype": "m.text", "body": "I am a fish" }`` +Alternatively, the membership state for this user in this room can be modified +directly by sending the following request to +``/rooms/<room id>/state/m.room.member/<url encoded user id>``:: -``m.emote`` - Required keys: - - ``body`` : "string" - The emote action to perform. - Optional keys: - None. - Example: - ``{ "msgtype": "m.emote", "body": "tries to come up with a witty explanation" }`` + { + "membership": "invite" + } -``m.image`` - Required keys: - - ``url`` : "string" - The URL to the image. - Optional keys: - - ``info`` : "string" - info : JSON object (ImageInfo) - The image info for - image referred to in ``url``. - - ``thumbnail_url`` : "string" - The URL to the thumbnail. - - ``thumbnail_info`` : JSON object (ImageInfo) - The image info for the - image referred to in ``thumbnail_url``. - - ``body`` : "string" - The alt text of the image, or some kind of content - description for accessibility e.g. "image attachment". +See the `Room events`_ section for more information on ``m.room.member``. - ImageInfo: - Information about an image:: - - { - "size" : integer (size of image in bytes), - "w" : integer (width of image in pixels), - "h" : integer (height of image in pixels), - "mimetype" : "string (e.g. image/jpeg)", - } +Leaving rooms +------------- +.. TODO kegan + - TODO: Grace period before deletion? + - TODO: Under what conditions should a room NOT be purged? -``m.audio`` - Required keys: - - ``url`` : "string" - The URL to the audio. - Optional keys: - - ``info`` : JSON object (AudioInfo) - The audio info for the audio - referred to in ``url``. - - ``body`` : "string" - A description of the audio e.g. "Bee Gees - Stayin' - Alive", or some kind of content description for accessibility e.g. - "audio attachment". - AudioInfo: - Information about a piece of audio:: - { - "mimetype" : "string (e.g. audio/aac)", - "size" : integer (size of audio in bytes), - "duration" : integer (duration of audio in milliseconds), - } +A user can leave a room to stop receiving events for that room. A user must +have joined the room before they are eligible to leave the room. If the room is +an "invite-only" room, they will need to be re-invited before they can re-join +the room. To leave a room, a request should be made to +|/rooms/<room_id>/leave|_ with:: -``m.video`` - Required keys: - - ``url`` : "string" - The URL to the video. - Optional keys: - - ``info`` : JSON object (VideoInfo) - The video info for the video - referred to in ``url``. - - ``body`` : "string" - A description of the video e.g. "Gangnam style", or - some kind of content description for accessibility e.g. "video - attachment". + {} - VideoInfo: - Information about a video:: +Alternatively, the membership state for this user in this room can be modified +directly by sending the following request to +``/rooms/<room id>/state/m.room.member/<url encoded user id>``:: - { - "mimetype" : "string (e.g. video/mp4)", - "size" : integer (size of video in bytes), - "duration" : integer (duration of video in milliseconds), - "w" : integer (width of video in pixels), - "h" : integer (height of video in pixels), - "thumbnail_url" : "string (URL to image)", - "thumbanil_info" : JSON object (ImageInfo) - } + { + "membership": "leave" + } -``m.location`` - Required keys: - - ``geo_uri`` : "string" - The geo URI representing the location. - Optional keys: - - ``thumbnail_url`` : "string" - The URL to a thumnail of the location - being represented. - - ``thumbnail_info`` : JSON object (ImageInfo) - The image info for the - image referred to in ``thumbnail_url``. - - ``body`` : "string" - A description of the location e.g. "Big Ben, - London, UK", or some kind of content description for accessibility e.g. - "location attachment". +See the `Room events`_ section for more information on ``m.room.member``. -The following keys can be attached to any ``m.room.message``: +Once a user has left a room, that room will no longer appear on the +|initialSync|_ API. Be aware that leaving a room is not equivalent to have +never been in that room. A user who has previously left a room still maintains +some residual state in that room. Their membership state will be marked as +``leave``. This contrasts with a user who has *never been invited or joined to +that room* who will not have any membership state for that room. - Optional keys: - - ``sender_ts`` : integer - A timestamp (ms resolution) representing the - wall-clock time when the message was sent from the client. +If all members in a room leave, that room becomes eligible for deletion. -Presence -======== -.. NOTE:: - This section is a work in progress. +Banning users in a room +----------------------- +A user may decide to ban another user in a room. 'Banning' forces the target +user to leave the room and prevents them from re-joining the room. A banned +user will not be treated as a joined user, and so will not be able to send or +receive events in the room. In order to ban someone, the user performing the +ban MUST have the required power level. To ban a user, a request should be made +to |/rooms/<room_id>/ban|_ with:: -Each user has the concept of presence information. This encodes the -"availability" of that user, suitable for display on other user's clients. This -is transmitted as an ``m.presence`` event and is one of the few events which -are sent *outside the context of a room*. The basic piece of presence -information is represented by the ``presence`` key, which is an enum of one of -the following: + { + "user_id": "<user id to ban" + "reason": "string: <reason for the ban>" + } + +Banning a user adjusts the banned member's membership state to ``ban`` and +adjusts the power level of this event to a level higher than the banned person. +Like with other membership changes, a user can directly adjust the target +member's state, by making a request to +``/rooms/<room id>/state/m.room.member/<user id>``:: - - ``online`` : The default state when the user is connected to an event - stream. - - ``unavailable`` : The user is not reachable at this time. - - ``offline`` : The user is not connected to an event stream. - - ``free_for_chat`` : The user is generally willing to receive messages - moreso than default. - - ``hidden`` : TODO. Behaves as offline, but allows the user to see the - client state anyway and generally interact with client features. + { + "membership": "ban" + } -This basic ``presence`` field applies to the user as a whole, regardless of how -many client devices they have connected. The home server should synchronise -this status choice among multiple devices to ensure the user gets a consistent -experience. +Events in a room +---------------- +Room events can be split into two categories: -In addition, the server maintains a timestamp of the last time it saw an active -action from the user; either sending a message to a room, or changing presence -state from a lower to a higher level of availability (thus: changing state from -``unavailable`` to ``online`` will count as an action for being active, whereas -in the other direction will not). This timestamp is presented via a key called -``last_active_ago``, which gives the relative number of miliseconds since the -message is generated/emitted, that the user was last seen active. +:State Events: + These are events which replace events that came before it, depending on a set + of unique keys. These keys are the event ``type`` and a ``state_key``. + Events with the same set of keys will be overwritten. Typically, state events + are used to store state, hence their name. -Idle Time ---------- -As well as the basic ``presence`` field, the presence information can also show -a sense of an "idle timer". This should be maintained individually by the -user's clients, and the home server can take the highest reported time as that -to report. When a user is offline, the home server can still report when the -user was last seen online. +:Non-state events: + These are events which cannot be overwritten after sending. The list of + events continues to grow as more events are sent. As this list grows, it + becomes necessary to provide a mechanism for navigating this list. Pagination + APIs are used to view the list of historical non-state events. Typically, + non-state events are used to send messages. -Transmission +This specification outlines several events, all with the event type prefix +``m.``. However, applications may wish to add their own type of event, and this +can be achieved using the REST API detailed in the following sections. If new +events are added, the event ``type`` key SHOULD follow the Java package naming +convention, e.g. ``com.example.myapp.event``. This ensures event types are +suitably namespaced for each application and reduces the risk of clashes. + +State events ------------ -.. NOTE:: - This section is a work in progress. +State events can be sent by ``PUT`` ing to +|/rooms/<room_id>/state/<event_type>/<state_key>|_. These events will be +overwritten if ``<room id>``, ``<event type>`` and ``<state key>`` all match. +If the state event has no ``state_key``, it can be omitted from the path. These +requests **cannot use transaction IDs** like other ``PUT`` paths because they +cannot be differentiated from the ``state_key``. Furthermore, ``POST`` is +unsupported on state paths. Valid requests look like:: -.. TODO: - - Transmitted as an EDU. - - Presence lists determine who to send to. + PUT /rooms/!roomid:domain/state/m.example.event + { "key" : "without a state key" } -Presence List -------------- -Each user's home server stores a "presence list" for that user. This stores a -list of other user IDs the user has chosen to add to it. To be added to this -list, the user being added must receive permission from the list owner. Once -granted, both user's HS(es) store this information. Since such subscriptions -are likely to be bidirectional, HSes may wish to automatically accept requests -when a reverse subscription already exists. + PUT /rooms/!roomid:domain/state/m.another.example.event/foo + { "key" : "with 'foo' as the state key" } -Presence and Permissions ------------------------- -For a viewing user to be allowed to see the presence information of a target -user, either: +In contrast, these requests are invalid:: - - The target user has allowed the viewing user to add them to their presence - list, or - - The two users share at least one room in common + POST /rooms/!roomid:domain/state/m.example.event/ + { "key" : "cannot use POST here" } -In the latter case, this allows for clients to display some minimal sense of -presence information in a user list for a room. + PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11 + { "key" : "txnIds are not supported" } -Typing notifications -==================== -.. NOTE:: - This section is a work in progress. +Care should be taken to avoid setting the wrong ``state key``:: -.. TODO Leo - - what is the event type. Are they bundled with other event types? If so, which. - - what are the valid keys / values. What do they represent. Any gotchas? - - Timeouts. How do they work, who sets them and how do they expire. Does one - have priority over another? Give examples. + PUT /rooms/!roomid:domain/state/m.another.example.event/11 + { "key" : "with '11' as the state key, but was probably intended to be a txnId" } -Voice over IP -============= -Matrix can also be used to set up VoIP calls. This is part of the core -specification, although is still in a very early stage. Voice (and video) over -Matrix is based on the WebRTC standards. +The ``state_key`` is often used to store state about individual users, by using +the user ID as the ``state_key`` value. For example:: -Call events are sent to a room, like any other event. This means that clients -must only send call events to rooms with exactly two participants as currently -the WebRTC standard is based around two-party communication. + PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Adomain.com + { "animal" : "cat", "reason": "fluffy" } -Events ------- -``m.call.invite`` -This event is sent by the caller when they wish to establish a call. +In some cases, there may be no need for a ``state_key``, so it can be omitted:: - Required keys: - - ``call_id`` : "string" - A unique identifier for the call - - ``offer`` : "offer object" - The session description - - ``version`` : "integer" - The version of the VoIP specification this - message adheres to. This specification is version 0. - - ``lifetime`` : "integer" - The time in milliseconds that the invite is - valid for. Once the invite age exceeds this value, clients should discard - it. They should also no longer show the call as awaiting an answer in the - UI. - - Optional keys: - None. - Example: - ``{ "version" : 0, "call_id": "12345", "offer": { "type" : "offer", "sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]" } }`` + PUT /rooms/!roomid:domain/state/m.room.bgd.color + { "color": "red", "hex": "#ff0000" } -``Offer Object`` - Required keys: - - ``type`` : "string" - The type of session description, in this case - 'offer' - - ``sdp`` : "string" - The SDP text of the session description +See `Room Events`_ for the ``m.`` event specification. -``m.call.candidates`` -This event is sent by callers after sending an invite and by the callee after -answering. Its purpose is to give the other party additional ICE candidates to -try using to communicate. +Non-state events +---------------- +Non-state events can be sent by sending a request to +|/rooms/<room_id>/send/<event_type>|_. These requests *can* use transaction +IDs and ``PUT``/``POST`` methods. Non-state events allow access to historical +events and pagination, making it best suited for sending messages. For +example:: - Required keys: - - ``call_id`` : "string" - The ID of the call this event relates to - - ``version`` : "integer" - The version of the VoIP specification this - messages adheres to. his specification is version 0. - - ``candidates`` : "array of candidate objects" - Array of object - describing the candidates. + POST /rooms/!roomid:domain/send/m.custom.example.message + { "text": "Hello world!" } -``Candidate Object`` + PUT /rooms/!roomid:domain/send/m.custom.example.message/11 + { "text": "Goodbye world!" } - Required Keys: - - ``sdpMid`` : "string" - The SDP media type this candidate is intended - for. - - ``sdpMLineIndex`` : "integer" - The index of the SDP 'm' line this - candidate is intended for - - ``candidate`` : "string" - The SDP 'a' line of the candidate +See `Room Events`_ for the ``m.`` event specification. -``m.call.answer`` +Syncing rooms +------------- +.. NOTE:: + This section is a work in progress. - Required keys: - - ``call_id`` : "string" - The ID of the call this event relates to - - ``version`` : "integer" - The version of the VoIP specification this - messages - - ``answer`` : "answer object" - Object giving the SDK answer +When a client logs in, they may have a list of rooms which they have already +joined. These rooms may also have a list of events associated with them. The +purpose of 'syncing' is to present the current room and event information in a +convenient, compact manner. The events returned are not limited to room events; +presence events will also be returned. There are two APIs provided: -``Answer Object`` + - |initialSync|_ : A global sync which will present room and event information + for all rooms the user has joined. - Required keys: - - ``type`` : "string" - The type of session description. 'answer' in this - case. - - ``sdp`` : "string" - The SDP text of the session description + - |/rooms/<room_id>/initialSync|_ : A sync scoped to a single room. Presents + room and event information for this room only. -``m.call.hangup`` -Sent by either party to signal their termination of the call. This can be sent -either once the call has has been established or before to abort the call. - - Required keys: - - ``call_id`` : "string" - The ID of the call this event relates to - - ``version`` : "integer" - The version of the VoIP specification this - messages - -Message Exchange ----------------- -A call is set up with messages exchanged as follows: +.. TODO kegan + - TODO: JSON response format for both types + - TODO: when would you use global? when would you use scoped? -:: +Getting events for a room +------------------------- +There are several APIs provided to ``GET`` events for a room: - Caller Callee - m.call.invite -----------> - m.call.candidate --------> - [more candidates events] - User answers call - <------ m.call.answer - [...] - <------ m.call.hangup - -Or a rejected call: +``/rooms/<room id>/state/<event type>/<state key>`` + Description: + Get the state event identified. + Response format: + A JSON object representing the state event **content**. + Example: + ``/rooms/!room:domain.com/state/m.room.name`` returns ``{ "name": "Room name" }`` -:: +|/rooms/<room_id>/state|_ + Description: + Get all state events for a room. + Response format: + ``[ { state event }, { state event }, ... ]`` + Example: + TODO - Caller Callee - m.call.invite -----------> - m.call.candidate --------> - [more candidates events] - User rejects call - <------- m.call.hangup -Calls are negotiated according to the WebRTC specification. +|/rooms/<room_id>/members|_ + Description: + Get all ``m.room.member`` state events. + Response format: + ``{ "start": "<token>", "end": "<token>", "chunk": [ { m.room.member event }, ... ] }`` + Example: + TODO +|/rooms/<room_id>/messages|_ + Description: + Get all ``m.room.message`` and ``m.room.member`` events. This API supports + pagination using ``from`` and ``to`` query parameters, coupled with the + ``start`` and ``end`` tokens from an |initialSync|_ API. + Response format: + ``{ "start": "<token>", "end": "<token>" }`` + Example: + TODO + +|/rooms/<room_id>/initialSync|_ + Description: + Get all relevant events for a room. This includes state events, paginated non-state + events and presence events. + Response format: + `` { TODO } `` + Example: + TODO -Glare ------ -This specification aims to address the problem of two users calling each other -at roughly the same time and their invites crossing on the wire. It is a far -better experience for the users if their calls are connected if it is clear -that their intention is to set up a call with one another. -In Matrix, calls are to rooms rather than users (even if those rooms may only -contain one other user) so we consider calls which are to the same room. +Room Events +=========== +.. NOTE:: + This section is a work in progress. -The rules for dealing with such a situation are as follows: +.. TODO dave? + - voip events? - - If an invite to a room is received whilst the client is preparing to send an - invite to the same room, the client should cancel its outgoing call and - instead automatically accept the incoming call on behalf of the user. - - If an invite to a room is received after the client has sent an invite to - the same room and is waiting for a response, the client should perform a - lexicographical comparison of the call IDs of the two calls and use the - lesser of the two calls, aborting the greater. If the incoming call is the - lesser, the client should accept this call on behalf of the user. +This specification outlines several standard event types, all of which are +prefixed with ``m.`` -The call setup should appear seamless to the user as if they had simply placed -a call and the other party had accepted. Thusly, any media stream that had been -setup for use on a call should be transferred and used for the call that -replaces it. - -Profiles -======== -.. NOTE:: - This section is a work in progress. +``m.room.name`` + Summary: + Set the human-readable name for the room. + Type: + State event + JSON format: + ``{ "name" : "string" }`` + Example: + ``{ "name" : "My Room" }`` + Description: + A room has an opaque room ID which is not human-friendly to read. A room + alias is human-friendly, but not all rooms have room aliases. The room name + is a human-friendly string designed to be displayed to the end-user. The + room name is not *unique*, as multiple rooms can have the same room name + set. The room name can also be set when creating a room using |createRoom|_ + with the ``name`` key. -.. TODO - - Metadata extensibility - - Changing profile info generates m.presence events ("presencelike") - - keys on m.presence are optional, except presence which is required - - m.room.member is populated with the current displayname at that point in time. - - That is added by the HS, not you. - - Display name changes also generates m.room.member with displayname key f.e. room - the user is in. +``m.room.topic`` + Summary: + Set a topic for the room. + Type: + State event + JSON format: + ``{ "topic" : "string" }`` + Example: + ``{ "topic" : "Welcome to the real world." }`` + Description: + A topic is a short message detailing what is currently being discussed in + the room. It can also be used as a way to display extra information about + the room, which may not be suitable for the room name. The room topic can + also be set when creating a room using |createRoom|_ with the ``topic`` + key. -Internally within Matrix users are referred to by their user ID, which is -typically a compact unique identifier. Profiles grant users the ability to see -human-readable names for other users that are in some way meaningful to them. -Additionally, profiles can publish additional information, such as the user's -age or location. +``m.room.member`` + Summary: + The current membership state of a user in the room. + Type: + State event + JSON format: + ``{ "membership" : "enum[ invite|join|leave|ban ]" }`` + Example: + ``{ "membership" : "join" }`` + Description: + Adjusts the membership state for a user in a room. It is preferable to use + the membership APIs (``/rooms/<room id>/invite`` etc) when performing + membership actions rather than adjusting the state directly as there are a + restricted set of valid transformations. For example, user A cannot force + user B to join a room, and trying to force this state change directly will + fail. See the `Rooms`_ section for how to use the membership APIs. -A Profile consists of a display name, an avatar picture, and a set of other -metadata fields that the user may wish to publish (email address, phone -numbers, website URLs, etc...). This specification puts no requirements on the -display name other than it being a valid unicode string. +``m.room.create`` + Summary: + The first event in the room. + Type: + State event + JSON format: + ``{ "creator": "string"}`` + Example: + ``{ "creator": "@user:example.com" }`` + Description: + This is the first event in a room and cannot be changed. It acts as the + root of all other events. +``m.room.join_rules`` + Summary: + Descripes how/if people are allowed to join. + Type: + State event + JSON format: + ``{ "join_rule": "enum [ public|knock|invite|private ]" }`` + Example: + ``{ "join_rule": "public" }`` + Description: + TODO : Use docs/models/rooms.rst + +``m.room.power_levels`` + Summary: + Defines the power levels of users in the room. + Type: + State event + JSON format: + ``{ "<user_id>": <int>, ..., "default": <int>}`` + Example: + ``{ "@user:example.com": 5, "@user2:example.com": 10, "default": 0 }`` + Description: + If a user is in the list, then they have the associated power level. + Otherwise they have the default level. If not ``default`` key is supplied, + it is assumed to be 0. +``m.room.add_state_level`` + Summary: + Defines the minimum power level a user needs to add state. + Type: + State event + JSON format: + ``{ "level": <int> }`` + Example: + ``{ "level": 5 }`` + Description: + To add a new piece of state to the room a user must have the given power + level. This does not apply to updating current state, which is goverened + by the ``required_power_level`` event key. + +``m.room.send_event_level`` + Summary: + Defines the minimum power level a user needs to send an event. + Type: + State event + JSON format: + ``{ "level": <int> }`` + Example: + ``{ "level": 0 }`` + Description: + To send a new event into the room a user must have at least this power + level. This allows ops to make the room read only by increasing this level, + or muting individual users by lowering their power level below this + threshold. -Registration and login -====================== +``m.room.ops_levels`` + Summary: + Defines the minimum power levels that a user must have before they can + kick and/or ban other users. + Type: + State event + JSON format: + ``{ "ban_level": <int>, "kick_level": <int> }`` + Example: + ``{ "ban_level": 5, "kick_level": 5 }`` + Description: + This defines who can ban and/or kick people in the room. Most of the time + ``ban_level`` will be greater than or equal to ``kick_level`` since + banning is more severe than kicking. -Clients must register with a home server in order to use Matrix. After -registering, the client will be given an access token which must be used in ALL -requests to that home server as a query parameter 'access_token'. +``m.room.aliases`` + Summary: + These state events are used to inform the room about what room aliases it + has. + Type: + State event + JSON format: + ``{ "aliases": ["string", ...] }`` + Example: + ``{ "aliases": ["#foo:example.com"] }`` + Description: + A server `may` inform the room that it has added or removed an alias for + the room. This is purely for informational purposes and may become stale. + Clients `should` check that the room alias is still valid before using it. + The ``state_key`` of the event is the homeserver which owns the room alias. -If the client has already registered, they need to be able to login to their -account. The home server may provide many different ways of logging in, such as -user/password auth, login via a social network (OAuth2), login by confirming a -token sent to their email address, etc. This specification does not define how -home servers should authorise their users who want to login to their existing -accounts, but instead defines the standard interface which implementations -should follow so that ANY client can login to ANY home server. Clients login -using the |login|_ API. Clients register using the |register|_ API. -Registration follows the same procedure as login, but the path requests are -sent to are different. +``m.room.message`` + Summary: + A message. + Type: + Non-state event + JSON format: + ``{ "msgtype": "string" }`` + Example: + ``{ "msgtype": "m.text", "body": "Testing" }`` + Description: + This event is used when sending messages in a room. Messages are not + limited to be text. The ``msgtype`` key outlines the type of message, e.g. + text, audio, image, video, etc. Whilst not required, the ``body`` key + SHOULD be used with every kind of ``msgtype`` as a fallback mechanism when + a client cannot render the message. For more information on the types of + messages which can be sent, see `m.room.message msgtypes`_. -The registration/login process breaks down into the following: - 1. Determine the requirements for logging in. - 2. Submit the login stage credentials. - 3. Get credentials or be told the next stage in the login process and repeat - step 2. - -As each home server may have different ways of logging in, the client needs to -know how they should login. All distinct login stages MUST have a corresponding -``type``. A ``type`` is a namespaced string which details the mechanism for -logging in. +``m.room.message.feedback`` + Summary: + A receipt for a message. + Type: + Non-state event + JSON format: + ``{ "type": "enum [ delivered|read ]", "target_event_id": "string" }`` + Example: + ``{ "type": "delivered", "target_event_id": "e3b2icys" }`` + Description: + Feedback events are events sent to acknowledge a message in some way. There + are two supported acknowledgements: ``delivered`` (sent when the event has + been received) and ``read`` (sent when the event has been observed by the + end-user). The ``target_event_id`` should reference the ``m.room.message`` + event being acknowledged. -A client may be able to login via multiple valid login flows, and should choose -a single flow when logging in. A flow is a series of login stages. The home -server MUST respond with all the valid login flows when requested:: +m.room.message msgtypes +----------------------- +Each ``m.room.message`` MUST have a ``msgtype`` key which identifies the type +of message being sent. Each type has their own required and optional keys, as +outlined below: - The client can login via 3 paths: 1a and 1b, 2a and 2b, or 3. The client should - select one of these paths. - - { - "flows": [ - { - "type": "<login type1a>", - "stages": [ "<login type 1a>", "<login type 1b>" ] - }, - { - "type": "<login type2a>", - "stages": [ "<login type 2a>", "<login type 2b>" ] - }, - { - "type": "<login type3>" - } - ] - } +``m.text`` + Required keys: + - ``body`` : "string" - The body of the message. + Optional keys: + None. + Example: + ``{ "msgtype": "m.text", "body": "I am a fish" }`` -After the login is completed, the client's fully-qualified user ID and a new -access token MUST be returned:: +``m.emote`` + Required keys: + - ``body`` : "string" - The emote action to perform. + Optional keys: + None. + Example: + ``{ "msgtype": "m.emote", "body": "tries to come up with a witty explanation" }`` - { - "user_id": "@user:matrix.org", - "access_token": "abcdef0123456789" - } +``m.image`` + Required keys: + - ``url`` : "string" - The URL to the image. + Optional keys: + - ``info`` : "string" - info : JSON object (ImageInfo) - The image info for + image referred to in ``url``. + - ``thumbnail_url`` : "string" - The URL to the thumbnail. + - ``thumbnail_info`` : JSON object (ImageInfo) - The image info for the + image referred to in ``thumbnail_url``. + - ``body`` : "string" - The alt text of the image, or some kind of content + description for accessibility e.g. "image attachment". -The ``user_id`` key is particularly useful if the home server wishes to support -localpart entry of usernames (e.g. "user" rather than "@user:matrix.org"), as -the client may not be able to determine its ``user_id`` in this case. + ImageInfo: + Information about an image:: + + { + "size" : integer (size of image in bytes), + "w" : integer (width of image in pixels), + "h" : integer (height of image in pixels), + "mimetype" : "string (e.g. image/jpeg)", + } -If a login has multiple requests, the home server may wish to create a session. -If a home server responds with a 'session' key to a request, clients MUST -submit it in subsequent requests until the login is completed:: +``m.audio`` + Required keys: + - ``url`` : "string" - The URL to the audio. + Optional keys: + - ``info`` : JSON object (AudioInfo) - The audio info for the audio + referred to in ``url``. + - ``body`` : "string" - A description of the audio e.g. "Bee Gees - Stayin' + Alive", or some kind of content description for accessibility e.g. + "audio attachment". + AudioInfo: + Information about a piece of audio:: - { - "session": "<session id>" - } + { + "mimetype" : "string (e.g. audio/aac)", + "size" : integer (size of audio in bytes), + "duration" : integer (duration of audio in milliseconds), + } -This specification defines the following login types: - - ``m.login.password`` - - ``m.login.oauth2`` - - ``m.login.email.code`` - - ``m.login.email.url`` - - ``m.login.email.identity`` +``m.video`` + Required keys: + - ``url`` : "string" - The URL to the video. + Optional keys: + - ``info`` : JSON object (VideoInfo) - The video info for the video + referred to in ``url``. + - ``body`` : "string" - A description of the video e.g. "Gangnam style", or + some kind of content description for accessibility e.g. "video + attachment". -Password-based --------------- -:Type: - ``m.login.password`` -:Description: - Login is supported via a username and password. + VideoInfo: + Information about a video:: -To respond to this type, reply with:: + { + "mimetype" : "string (e.g. video/mp4)", + "size" : integer (size of video in bytes), + "duration" : integer (duration of video in milliseconds), + "w" : integer (width of video in pixels), + "h" : integer (height of video in pixels), + "thumbnail_url" : "string (URL to image)", + "thumbanil_info" : JSON object (ImageInfo) + } - { - "type": "m.login.password", - "user": "<user_id or user localpart>", - "password": "<password>" - } +``m.location`` + Required keys: + - ``geo_uri`` : "string" - The geo URI representing the location. + Optional keys: + - ``thumbnail_url`` : "string" - The URL to a thumnail of the location + being represented. + - ``thumbnail_info`` : JSON object (ImageInfo) - The image info for the + image referred to in ``thumbnail_url``. + - ``body`` : "string" - A description of the location e.g. "Big Ben, + London, UK", or some kind of content description for accessibility e.g. + "location attachment". -The home server MUST respond with either new credentials, the next stage of the -login process, or a standard error response. +The following keys can be attached to any ``m.room.message``: -OAuth2-based ------------- -:Type: - ``m.login.oauth2`` -:Description: - Login is supported via OAuth2 URLs. This login consists of multiple requests. + Optional keys: + - ``sender_ts`` : integer - A timestamp (ms resolution) representing the + wall-clock time when the message was sent from the client. -To respond to this type, reply with:: +Presence +======== +.. NOTE:: + This section is a work in progress. - { - "type": "m.login.oauth2", - "user": "<user_id or user localpart>" - } +Each user has the concept of presence information. This encodes the +"availability" of that user, suitable for display on other user's clients. This +is transmitted as an ``m.presence`` event and is one of the few events which +are sent *outside the context of a room*. The basic piece of presence +information is represented by the ``presence`` key, which is an enum of one of +the following: -The server MUST respond with:: + - ``online`` : The default state when the user is connected to an event + stream. + - ``unavailable`` : The user is not reachable at this time. + - ``offline`` : The user is not connected to an event stream. + - ``free_for_chat`` : The user is generally willing to receive messages + moreso than default. + - ``hidden`` : TODO. Behaves as offline, but allows the user to see the + client state anyway and generally interact with client features. - { - "uri": <Authorization Request URI OR service selection URI> - } +This basic ``presence`` field applies to the user as a whole, regardless of how +many client devices they have connected. The home server should synchronise +this status choice among multiple devices to ensure the user gets a consistent +experience. -The home server acts as a 'confidential' client for the purposes of OAuth2. If -the uri is a ``sevice selection URI``, it MUST point to a webpage which prompts -the user to choose which service to authorize with. On selection of a service, -this MUST link through to an ``Authorization Request URI``. If there is only 1 -service which the home server accepts when logging in, this indirection can be -skipped and the "uri" key can be the ``Authorization Request URI``. +In addition, the server maintains a timestamp of the last time it saw an active +action from the user; either sending a message to a room, or changing presence +state from a lower to a higher level of availability (thus: changing state from +``unavailable`` to ``online`` will count as an action for being active, whereas +in the other direction will not). This timestamp is presented via a key called +``last_active_ago``, which gives the relative number of miliseconds since the +message is generated/emitted, that the user was last seen active. -The client then visits the ``Authorization Request URI``, which then shows the -OAuth2 Allow/Deny prompt. Hitting 'Allow' returns the ``redirect URI`` with the -auth code. Home servers can choose any path for the ``redirect URI``. The -client should visit the ``redirect URI``, which will then finish the OAuth2 -login process, granting the home server an access token for the chosen service. -When the home server gets this access token, it verifies that the cilent has -authorised with the 3rd party, and can now complete the login. The OAuth2 -``redirect URI`` (with auth code) MUST respond with either new credentials, the -next stage of the login process, or a standard error response. - -For example, if a home server accepts OAuth2 from Google, it would return the -Authorization Request URI for Google:: +Idle Time +--------- +As well as the basic ``presence`` field, the presence information can also show +a sense of an "idle timer". This should be maintained individually by the +user's clients, and the home server can take the highest reported time as that +to report. When a user is offline, the home server can still report when the +user was last seen online. - { - "uri": "https://accounts.google.com/o/oauth2/auth?response_type=code& - client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos" - } +Transmission +------------ +.. NOTE:: + This section is a work in progress. -The client then visits this URI and authorizes the home server. The client then -visits the REDIRECT_URI with the auth code= query parameter which returns:: +.. TODO: + - Transmitted as an EDU. + - Presence lists determine who to send to. - { - "user_id": "@user:matrix.org", - "access_token": "0123456789abcdef" - } +Presence List +------------- +Each user's home server stores a "presence list" for that user. This stores a +list of other user IDs the user has chosen to add to it. To be added to this +list, the user being added must receive permission from the list owner. Once +granted, both user's HS(es) store this information. Since such subscriptions +are likely to be bidirectional, HSes may wish to automatically accept requests +when a reverse subscription already exists. -Email-based (code) ------------------- -:Type: - ``m.login.email.code`` -:Description: - Login is supported by typing in a code which is sent in an email. This login - consists of multiple requests. +Presence and Permissions +------------------------ +For a viewing user to be allowed to see the presence information of a target +user, either: -To respond to this type, reply with:: + - The target user has allowed the viewing user to add them to their presence + list, or + - The two users share at least one room in common - { - "type": "m.login.email.code", - "user": "<user_id or user localpart>", - "email": "<email address>" - } +In the latter case, this allows for clients to display some minimal sense of +presence information in a user list for a room. + +Typing notifications +==================== +.. NOTE:: + This section is a work in progress. -After validating the email address, the home server MUST send an email -containing an authentication code and return:: +.. TODO Leo + - what is the event type. Are they bundled with other event types? If so, which. + - what are the valid keys / values. What do they represent. Any gotchas? + - Timeouts. How do they work, who sets them and how do they expire. Does one + have priority over another? Give examples. - { - "type": "m.login.email.code", - "session": "<session id>" - } +Voice over IP +============= +Matrix can also be used to set up VoIP calls. This is part of the core +specification, although is still in a very early stage. Voice (and video) over +Matrix is based on the WebRTC standards. -The second request in this login stage involves sending this authentication -code:: +Call events are sent to a room, like any other event. This means that clients +must only send call events to rooms with exactly two participants as currently +the WebRTC standard is based around two-party communication. - { - "type": "m.login.email.code", - "session": "<session id>", - "code": "<code in email sent>" - } +Events +------ +``m.call.invite`` +This event is sent by the caller when they wish to establish a call. -The home server MUST respond to this with either new credentials, the next -stage of the login process, or a standard error response. + Required keys: + - ``call_id`` : "string" - A unique identifier for the call + - ``offer`` : "offer object" - The session description + - ``version`` : "integer" - The version of the VoIP specification this + message adheres to. This specification is version 0. + - ``lifetime`` : "integer" - The time in milliseconds that the invite is + valid for. Once the invite age exceeds this value, clients should discard + it. They should also no longer show the call as awaiting an answer in the + UI. + + Optional keys: + None. + Example: + ``{ "version" : 0, "call_id": "12345", "offer": { "type" : "offer", "sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]" } }`` -Email-based (url) ------------------ -:Type: - ``m.login.email.url`` -:Description: - Login is supported by clicking on a URL in an email. This login consists of - multiple requests. +``Offer Object`` + Required keys: + - ``type`` : "string" - The type of session description, in this case + 'offer' + - ``sdp`` : "string" - The SDP text of the session description -To respond to this type, reply with:: +``m.call.candidates`` +This event is sent by callers after sending an invite and by the callee after +answering. Its purpose is to give the other party additional ICE candidates to +try using to communicate. - { - "type": "m.login.email.url", - "user": "<user_id or user localpart>", - "email": "<email address>" - } + Required keys: + - ``call_id`` : "string" - The ID of the call this event relates to + - ``version`` : "integer" - The version of the VoIP specification this + messages adheres to. his specification is version 0. + - ``candidates`` : "array of candidate objects" - Array of object + describing the candidates. -After validating the email address, the home server MUST send an email -containing an authentication URL and return:: +``Candidate Object`` - { - "type": "m.login.email.url", - "session": "<session id>" - } + Required Keys: + - ``sdpMid`` : "string" - The SDP media type this candidate is intended + for. + - ``sdpMLineIndex`` : "integer" - The index of the SDP 'm' line this + candidate is intended for + - ``candidate`` : "string" - The SDP 'a' line of the candidate -The email contains a URL which must be clicked. After it has been clicked, the -client should perform another request:: +``m.call.answer`` - { - "type": "m.login.email.url", - "session": "<session id>" - } + Required keys: + - ``call_id`` : "string" - The ID of the call this event relates to + - ``version`` : "integer" - The version of the VoIP specification this + messages + - ``answer`` : "answer object" - Object giving the SDK answer -The home server MUST respond to this with either new credentials, the next -stage of the login process, or a standard error response. +``Answer Object`` -A common client implementation will be to periodically poll until the link is -clicked. If the link has not been visited yet, a standard error response with -an errcode of ``M_LOGIN_EMAIL_URL_NOT_YET`` should be returned. + Required keys: + - ``type`` : "string" - The type of session description. 'answer' in this + case. + - ``sdp`` : "string" - The SDP text of the session description +``m.call.hangup`` +Sent by either party to signal their termination of the call. This can be sent +either once the call has has been established or before to abort the call. -Email-based (identity server) ------------------------------ -:Type: - ``m.login.email.identity`` -:Description: - Login is supported by authorising an email address with an identity server. + Required keys: + - ``call_id`` : "string" - The ID of the call this event relates to + - ``version`` : "integer" - The version of the VoIP specification this + messages -Prior to submitting this, the client should authenticate with an identity -server. After authenticating, the session information should be submitted to -the home server. +Message Exchange +---------------- +A call is set up with messages exchanged as follows: -To respond to this type, reply with:: +:: - { - "type": "m.login.email.identity", - "threepidCreds": [ - { - "sid": "<identity server session id>", - "clientSecret": "<identity server client secret>", - "idServer": "<url of identity server authed with, e.g. 'matrix.org:8090'>" - } - ] - } + Caller Callee + m.call.invite -----------> + m.call.candidate --------> + [more candidates events] + User answers call + <------ m.call.answer + [...] + <------ m.call.hangup + +Or a rejected call: +:: + Caller Callee + m.call.invite -----------> + m.call.candidate --------> + [more candidates events] + User rejects call + <------- m.call.hangup -N-Factor Authentication ------------------------ -Multiple login stages can be combined to create N-factor authentication during -login. +Calls are negotiated according to the WebRTC specification. -This can be achieved by responding with the ``next`` login type on completion -of a previous login stage:: - { - "next": "<next login type>" - } +Glare +----- +This specification aims to address the problem of two users calling each other +at roughly the same time and their invites crossing on the wire. It is a far +better experience for the users if their calls are connected if it is clear +that their intention is to set up a call with one another. -If a home server implements N-factor authentication, it MUST respond with all -``stages`` when initially queried for their login requirements:: +In Matrix, calls are to rooms rather than users (even if those rooms may only +contain one other user) so we consider calls which are to the same room. - { - "type": "<1st login type>", - "stages": [ <1st login type>, <2nd login type>, ... , <Nth login type> ] - } +The rules for dealing with such a situation are as follows: -This can be represented conceptually as:: + - If an invite to a room is received whilst the client is preparing to send an + invite to the same room, the client should cancel its outgoing call and + instead automatically accept the incoming call on behalf of the user. + - If an invite to a room is received after the client has sent an invite to + the same room and is waiting for a response, the client should perform a + lexicographical comparison of the call IDs of the two calls and use the + lesser of the two calls, aborting the greater. If the incoming call is the + lesser, the client should accept this call on behalf of the user. - _______________________ - | Login Stage 1 | - | type: "<login type1>" | - | ___________________ | - | |_Request_1_________| | <-- Returns "session" key which is used throughout. - | ___________________ | - | |_Request_2_________| | <-- Returns a "next" value of "login type2" - |_______________________| - | - | - _________V_____________ - | Login Stage 2 | - | type: "<login type2>" | - | ___________________ | - | |_Request_1_________| | - | ___________________ | - | |_Request_2_________| | - | ___________________ | - | |_Request_3_________| | <-- Returns a "next" value of "login type3" - |_______________________| - | - | - _________V_____________ - | Login Stage 3 | - | type: "<login type3>" | - | ___________________ | - | |_Request_1_________| | <-- Returns user credentials - |_______________________| +The call setup should appear seamless to the user as if they had simply placed +a call and the other party had accepted. Thusly, any media stream that had been +setup for use on a call should be transferred and used for the call that +replaces it. + +Profiles +======== +.. NOTE:: + This section is a work in progress. -Fallback --------- -Clients cannot be expected to be able to know how to process every single login -type. If a client determines it does not know how to handle a given login type, -it should request a login fallback page:: +.. TODO + - Metadata extensibility + - Changing profile info generates m.presence events ("presencelike") + - keys on m.presence are optional, except presence which is required + - m.room.member is populated with the current displayname at that point in time. + - That is added by the HS, not you. + - Display name changes also generates m.room.member with displayname key f.e. room + the user is in. - GET matrix/client/api/v1/login/fallback +Internally within Matrix users are referred to by their user ID, which is +typically a compact unique identifier. Profiles grant users the ability to see +human-readable names for other users that are in some way meaningful to them. +Additionally, profiles can publish additional information, such as the user's +age or location. + +A Profile consists of a display name, an avatar picture, and a set of other +metadata fields that the user may wish to publish (email address, phone +numbers, website URLs, etc...). This specification puts no requirements on the +display name other than it being a valid unicode string. -This MUST return an HTML page which can perform the entire login process. Identity ========