summary refs log tree commit diff
path: root/docs/specification.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/specification.rst')
-rw-r--r--docs/specification.rst640
1 files changed, 594 insertions, 46 deletions
diff --git a/docs/specification.rst b/docs/specification.rst

index c1559c886c..2b47009187 100644 --- a/docs/specification.rst +++ b/docs/specification.rst
@@ -35,8 +35,8 @@ namespaced to the home server which allocated the account and looks like:: @localpart:domain -The ``localpart`` of a user ID may be a user name, or an opaque ID identifying this user. - +The ``localpart`` of a user ID may be a user name, or an opaque ID identifying this user. They are +case-insensitive. A "Home Server" is a server which provides C-S APIs and has the ability to federate with other HSes. It is typically responsible for multiple clients. "Federation" is the term used to describe the @@ -60,7 +60,8 @@ identified via a "Room ID", which look like:: There is exactly one room ID for each room. Whilst the room ID does contain a domain, it is simply for namespacing room IDs. The room does NOT reside on the -domain specified. Room IDs are not meant to be human readable. +domain specified. Room IDs are not meant to be human readable. They ARE +case-sensitive. The following diagram shows an ``m.room.message`` event being sent in the room ``!qporfwt:matrix.org``:: @@ -102,10 +103,10 @@ Each room can also have multiple "Room Aliases", which looks like:: A room alias "points" to a room ID. The room ID the alias is pointing to can be obtained by visiting the domain specified. Room aliases are designed to be human readable strings -which can be used to publicise rooms. Note that the mapping from a room alias to a -room ID is not fixed, and may change over time to point to a different room ID. For this -reason, Clients SHOULD resolve the room alias to a room ID once and then use that ID on -subsequent requests. +which can be used to publicise rooms. They are case-insensitive. Note that the mapping +from a room alias to a room ID is not fixed, and may change over time to point to a +different room ID. For this reason, Clients SHOULD resolve the room alias to a room ID +once and then use that ID on subsequent requests. :: @@ -132,49 +133,567 @@ Identity API Standards ------------- -- All HTTP[S] -- Uses JSON as HTTP bodies -- Standard error response format { errcode: M_WHATEVER, error: "some message" } -- C-S API provides POST for operations, or PUT with txn IDs. Explain txn IDs. +All communication in Matrix is performed over HTTP[S] using a Content-Type of ``application/json``. +Any errors which occur on the Matrix API level MUST return a "standard error response". This is a +JSON object which looks like:: + + { + "errcode": "<error code>", + "error": "<error message>" + } + +The ``error`` string will be a human-readable error message, usually a sentence +explaining what went wrong. The ``errcode`` string will be a unique string which can be +used to handle an error message e.g. ``M_FORBIDDEN``. These error codes should have their +namespace first in ALL CAPS, followed by a single _. For example, if there was a custom +namespace ``com.mydomain.here``, and a ``FORBIDDEN`` code, the error code should look +like ``COM.MYDOMAIN.HERE_FORBIDDEN``. There may be additional keys depending on +the error, but the keys ``error`` and ``errcode`` MUST always be present. + +Some standard error codes are below: + +:``M_FORBIDDEN``: + Forbidden access, e.g. joining a room without permission, failed login. + +:``M_UNKNOWN_TOKEN``: + The access token specified was not recognised. + +:``M_BAD_JSON``: + Request contained valid JSON, but it was malformed in some way, e.g. missing + required keys, invalid values for keys. + +:``M_NOT_JSON``: + Request did not contain valid JSON. + +:``M_NOT_FOUND``: + No resource was found for this request. + +:``M_LIMIT_EXCEEDED``: + Too many requests have been sent in a short period of time. Wait a while then + try again. + +Some requests have unique error codes: + +:``M_USER_IN_USE``: + Encountered when trying to register a user ID which has been taken. + +:``M_ROOM_IN_USE``: + Encountered when trying to create a room which has been taken. + +:``M_BAD_PAGINATION``: + Encountered when specifying bad pagination query parameters. + +:``M_LOGIN_EMAIL_URL_NOT_YET``: + Encountered when polling for an email link which has not been clicked yet. + +The C-S API typically uses ``HTTP POST`` to submit requests. This means these requests +are not idempotent. The C-S API also allows ``HTTP PUT`` to make requests idempotent. +In order to use a ``PUT``, paths should be suffixed with ``/{txnId}``. ``{txnId}`` is a +client-generated transaction ID which identifies the request. Crucially, it **only** +serves to identify new requests from retransmits. After the request has finished, the +``{txnId}`` value should be changed (how is not specified, it could be a monotonically +increasing integer, etc). It is preferable to use ``HTTP PUT`` to make sure requests to +send messages do not get sent more than once should clients need to retransmit requests. + +Valid requests look like:: + + POST /some/path/here + { + "key": "This is a post." + } + + PUT /some/path/here/11 + { + "key": "This is a put with a txnId of 11." + } + +In contrast, these are invalid requests:: + + POST /some/path/here/11 + { + "key": "This is a post, but it has a txnId." + } + + PUT /some/path/here + { + "key": "This is a put but it is missing a txnId." + } + + + +- TODO: All strings everywhere are UTF-8 + + Receiving live updates on a client ---------------------------------- -- C-S longpoll event stream -- Concept of start/end tokens. -- Mention /initialSync to get token. +Clients can receive new events by long-polling the home server. This will hold open the +HTTP connection for a short period of time waiting for new events, returning early if an +event occurs. This is called the "Event Stream". All events which the client is authorised +to view will appear in the event stream. When the stream is closed, an ``end`` token is +returned. This token can be used in the next request to continue where the client left off. +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 ===== -- How are they created? PDU anchor point: "root of the tree". -- Adding / removing aliases. -- Invite/join dance -- State and non-state data (+extensibility) -TODO : Room permissions / config / power levels. +Creation +-------- +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`` + +``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``. + +``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``. + +Example:: -Messages -======== + { + "visibility": "public", + "room_alias_name": "the pub", + "name": "The Grand Duke Pub", + "topic": "All about happy hour" + } -This specification outlines several standard event types, all of which are -prefixed with ``m.`` +- TODO: This creates a room creation event which serves as the root of the PDU graph for this room. +- TODO: Keys for speccing a room name / room topic / invite these users? + +Modifying aliases +----------------- +- path to edit aliases +- format when retrieving list of aliases. NOT complete list. +- format for adding aliases. + +Permissions +----------- +- 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. + + +Joining rooms +------------- +- TODO: What does the home server have to do to join a user to a room? -State messages +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:: + + {} + +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:: + + { + "room_id": "!roomid:domain" + } + +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>``:: + + { + "membership": "join" + } + +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 -------------- -- m.room.name -- m.room.topic -- m.room.member -- m.room.config -- m.room.invite_join +- 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? + +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: + + - TODO Room config invite only value explanation + - TODO Room config free-to-join value explanation + +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:: -What are they, when are they used, what do they contain, how should they be used + { + "user_id": "<user id to invite>" + } -Non-state messages ------------------- -- m.room.message -- m.room.message.feedback (and compressed format) +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>``:: + + { + "membership": "invite" + } + +See the "Room events" section for more information on ``m.room.member``. + +- TODO: In what circumstances will this NOT be equivalent to ``/invite``? + +Leaving rooms +------------- +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:: + + {} -What are they, when are they used, what do they contain, how should they be used +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>``:: + + { + "membership": "leave" + } + +See the "Room events" section for more information on ``m.room.member``. + +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. + +If all members in a room leave, that room becomes eligible for deletion. + - TODO: Grace period before deletion? + - TODO: Under what conditions should a room NOT be purged? + +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:: + + { + "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>``:: + + { + "membership": "ban" + } + +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:: + + PUT /rooms/!roomid:domain/state/m.example.event + { "key" : "without a state key" } + + PUT /rooms/!roomid:domain/state/m.another.example.event/foo + { "key" : "with 'foo' as the state key" } + +In contrast, these requests are invalid:: + + POST /rooms/!roomid:domain/state/m.example.event/ + { "key" : "cannot use POST here" } + + PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11 + { "key" : "txnIds are not supported" } + +Care should be taken to avoid setting the wrong ``state key``:: + + 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" } + +The ``state_key`` is often used to store state about individual users, by using the user ID as the +``state_key`` value. For example:: + + PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Adomain.com + { "animal" : "cat", "reason": "fluffy" } + +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" } + +See "Room Events" for the ``m.`` event specification. + +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:: + + POST /rooms/!roomid:domain/send/m.custom.example.message + { "text": "Hello world!" } + + PUT /rooms/!roomid:domain/send/m.custom.example.message/11 + { "text": "Goodbye world!" } + +See "Room Events" for the ``m.`` event specification. + +Syncing rooms +------------- +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. + + - ``/rooms/<room id>/initialSync`` : A sync scoped to a single room. Presents room and event + information for this room only. + +- 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: + +``/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 + + +``/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`` events. + Response format: + ``{ TODO }`` + 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 + + +Room Events +=========== +- voip events? + +This specification outlines several standard event types, all of which are +prefixed with ``m.`` + +``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. + +``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. + +``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. + +``m.room.config`` + Summary: + The room config. + Type: + State event + JSON format: + TODO + Example: + TODO + Description: + TODO + +``m.room.invite_join`` + Summary: + TODO. + Type: + State event + JSON format: + TODO + Example: + TODO + Description: + TODO + +``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". + +``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. m.room.message msgtypes ----------------------- @@ -285,7 +804,7 @@ 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 ``state`` key, which is an enum of one of the following: +is represented by the ``presence`` key, which is an enum of one of the following: - ``online`` : The default state when the user is connected to an event stream. - ``unavailable`` : The user is not reachable at this time. @@ -295,18 +814,26 @@ is represented by the ``state`` key, which is an enum of one of the following: - ``hidden`` : TODO. Behaves as offline, but allows the user to see the client state anyway and generally interact with client features. -This basic ``state`` field applies to the user as a whole, regardless of how many +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. +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. + Idle Time --------- -As well as the basic ``state`` 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. +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. Transmission ------------ @@ -336,11 +863,23 @@ presence information in a user list for a room. Typing notifications ==================== +- 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. TODO : Leo Voice over IP ============= +- what are the event types. +- what are the valid keys/values. What do they represent. Any gotchas? +- In what sequence should the events be sent? +- How do you accept / decline inbound calls? How do you make outbound calls? + Give examples. +- How does negotiation work? Give examples. +- How do you hang up? +- What does call log information look like e.g. duration of call? TODO : Dave @@ -358,8 +897,12 @@ numbers, website URLs, etc...). This specification puts no requirements on the display name other than it being a valid unicode string. - Metadata extensibility -- Bundled with which events? e.g. m.room.member -- Generate own events? What type? +- 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. Registration and login ====================== @@ -823,11 +1366,16 @@ TODO Content repository ================== -- thumbnail paths +- path to upload +- format for thumbnail paths, mention what it is protecting against. +- content size limit and associated M_ERROR. Address book repository ======================= -- format +- format: POST(?) wodges of json, some possible processing, then return wodges of json on GET. +- processing may remove dupes, merge contacts, pepper with extra info (e.g. matrix-ability of + contacts), etc. +- Standard json format for contacts? Piggy back off vcards? Glossary