From 068b348e7ea79677750854d828ad1c7bbf8abba9 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 28 Aug 2014 17:40:04 +0100 Subject: Start fleshing out architecture section. Moar .rst formatting! Reword some copypastaed sections to be terser. --- docs/specification.rst | 252 ++++++++++++++++++++++++++++--------------------- 1 file changed, 146 insertions(+), 106 deletions(-) (limited to 'docs') diff --git a/docs/specification.rst b/docs/specification.rst index fa085bac27..35fbd4dd22 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -5,16 +5,18 @@ TODO(Introduction) : Matthew - Similar to intro paragraph from README. - Explaining the overall mission, what this spec describes... - "What is Matrix?" + - Draw parallels with email? Architecture ============ - -- Basic structure: What are clients/home servers and what are their - responsibilities? What are events. +- Sending a message from A to B :: - { Matrix clients } { Matrix clients } + How data flows between clients + ============================== + + { Matrix client A } { Matrix client B } ^ | ^ | | events | | events | | V | V @@ -22,22 +24,87 @@ Architecture | |---------( HTTP )---------->| | | Home Server | | Home Server | | |<--------( HTTP )-----------| | - +------------------+ +------------------+ - -- How do identity servers fit in? 3PIDs? Users? Aliases -- Pattern of the APIs (HTTP/JSON, REST + txns) -- Standard error response format. -- C-S Event stream - -Rooms -===== + +------------------+ Federation +------------------+ + +- Client is an end-user (web app, mobile app) which uses C-S APIs to talk to the home server. + A given client is typically responsible for a single user. +- Home server provides C-S APIs and has the ability to federate with other HSes. + Typically responsible for N clients. +- Federation's purpose is to share content between interested HSes; no SPOF. +- Events are actions within the system. Typically each action (e.g. sending a message) + correlates with exactly one event. Each event has a ``type`` string. +- ``type`` values SHOULD be namespaced according to standard Java package naming conventions, + with a ``.`` delimiter e.g. ``com.example.myapp.event`` +- Events are typically send in the context of a room. + +Room structure +-------------- A room is a conceptual place where users can send and receive messages. Rooms can be created, joined and left. Messages are sent to a room, and all participants in that room will receive the message. Rooms are uniquely -identified via a room ID. There is exactly one room ID for each room. +identified via a room ID. There is exactly one room ID for each room. Each +room can also have an alias. Each room can have many aliases. + +:: -- Aliases + How events flow in rooms + ======================== + + { @alice:matrix.org } { @bob:domain.com } + | ^ + | | + Room ID: !qporfwt:matrix.org Room ID: !qporfwt:matrix.org + Event type: m.room.message Event type: m.room.message + Content: { JSON object } Content: { JSON object } + | | + V | + +------------------+ +------------------+ + | Home Server | | Home Server | + | matrix.org |<-------Federation--------->| domain.com | + +------------------+ +------------------+ + Room ID: !qporfwt:matrix.org Room ID: !qporfwt:matrix.org + Servers: matrix.org, domain.com Servers: matrix.org, domain.com + Members: Members: + - @alice:matrix.org - @alice:matrix.org + - @bob:domain.com - @bob:domain.com + + +- Room IDs MUST have ! prefix; looks like !foo:domain - domain is simply for namespacing, + the room does NOT reside on domain. NOT human readable. +- Room Aliases MUST have # prefix; looks like #foo:domain - domain indicates where this + alias can be mapped to a room ID. Key point: human readable / friendly. +- User IDs MUST have @ prefix; looks like @foo:domain - domain indicates the user's home + server. +- Aliases can be queried on the domain they specify, which will return a room ID if a + mapping exists. These mappings can change. + +Identity +-------- +- Identity in relation to 3PIDs. Discovery of users based on 3PIDs. +- Identity servers; trusted clique of servers which replicate content. +- They govern the mapping of 3PIDs to user IDs and the creation of said mappings. +- Not strictly required in order to communicate. + + +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. + +Receiving live updates on a client +---------------------------------- +- C-S longpoll event stream +- Concept of start/end tokens. +- Mention /initialSync to get token. + + +Rooms +===== +- How are they created? +- Adding / removing aliases. - Invite/join dance - State and non-state data (+extensibility) @@ -46,10 +113,8 @@ TODO : Room permissions / config / power levels. Messages ======== -This specification outlines several standard message types, all of which are -prefixed with "m.". - -- Namespacing? +This specification outlines several standard event types, all of which are +prefixed with ``m.`` State messages -------------- @@ -174,88 +239,59 @@ The following keys can be attached to any ``m.room.message``: Presence ======== -Each user has the concept of Presence information. This encodes a sense of the -"availability" of that user, suitable for display on other user's clients. - -The basic piece of presence information is an enumeration of a small set of -state; such as "free to chat", "online", "busy", or "offline". The default state -unless the user changes it is "online". Lower states suggest some amount of -decreased availability from normal, which might have some client-side effect -like muting notification sounds and suggests to other users not to bother them -unless it is urgent. Equally, the "free to chat" state exists to let the user -announce their general willingness to receive messages moreso than default. - -Home servers should also allow a user to set their state as "hidden" - a state -which behaves as offline, but allows the user to see the client state anyway and -generally interact with client features such as reading message history or -accessing contacts in the address book. - -This basic state field applies to the user as a whole, regardless of how many +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: + + - ``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. + +This basic ``state`` 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. Idle Time --------- -As well as the basic state field, the presence information can also show a sense +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 homeserver can take the highest reported time as that to -report. Likely this should be presented in fairly coarse granularity; possibly -being limited to letting the home server automatically switch from a "free to -chat" or "online" mode into "idle". - -When a user is offline, the Home Server can still report when the user was last -seen online, again perhaps in a somewhat coarse manner. +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. -Device Type ------------ -Client devices that may limit the user experience somewhat (such as "mobile" -devices with limited ability to type on a real keyboard or read large amounts of -text) should report this to the home server, as this is also useful information -to report as "presence" if the user cannot be expected to provide a good typed -response to messages. - -- m.presence and enums (when should they be used) +Transmission +------------ +- Transmitted as an EDU. +- Presence lists determine who to send to. 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 (remembering any ACL -Pointer if appropriate). - -To be added to a contact list, the user being added must grant permission. Once -granted, both user's HS(es) store this information, as it allows the user who -has added the contact some more abilities; see below. Since such subscriptions +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. -As a convenience, presence lists should support the ability to collect users -into groups, which could allow things like inviting the entire group to a new -("ad-hoc") chat room, or easy interaction with the profile information ACL -implementation of the HS. - Presence and Permissions ------------------------ For a viewing user to be allowed to see the presence information of a target -user, either +user, either: - * The target user has allowed the viewing user to add them to their presence + - 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 + - The two users share at least one room in common In the latter case, this allows for clients to display some minimal sense of presence information in a user list for a room. -Home servers can also use the user's choice of presence state as a signal for -how to handle new private one-to-one chat message requests. For example, it -might decide: - - - "free to chat": accept anything - - "online": accept from anyone in my address book list - - "busy": accept from anyone in this "important people" group in my address - book list - Typing notifications ==================== @@ -274,18 +310,14 @@ human-friendly string. 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. -It is also conceivable that since we are attempting to provide a -worldwide-applicable messaging system, that users may wish to present different -subsets of information in their profile to different other people, from a -privacy and permissions perspective. - 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. +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? Registration and login ====================== @@ -312,8 +344,8 @@ The login process breaks down into the following: 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. +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. 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 @@ -359,17 +391,17 @@ subsequent requests until the login is completed:: } This specification defines the following login types: - - m.login.password - - m.login.oauth2 - - m.login.email.code - - m.login.email.url + - ``m.login.password`` + - ``m.login.oauth2`` + - ``m.login.email.code`` + - ``m.login.email.url`` Password-based -------------- -Type: - "m.login.password" -Description: +:Type: + m.login.password +:Description: Login is supported via a username and password. To respond to this type, reply with:: @@ -385,9 +417,9 @@ process, or a standard error response. OAuth2-based ------------ -Type: - "m.login.oauth2" -Description: +:Type: + m.login.oauth2 +:Description: Login is supported via OAuth2 URLs. This login consists of multiple requests. To respond to this type, reply with:: @@ -438,9 +470,9 @@ visits the REDIRECT_URI with the auth code= query parameter which returns:: Email-based (code) ------------------ -Type: - "m.login.email.code" -Description: +: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. @@ -473,9 +505,9 @@ the login process, or a standard error response. Email-based (url) ----------------- -Type: - "m.login.email.url" -Description: +:Type: + m.login.email.url +:Description: Login is supported by clicking on a URL in an email. This login consists of multiple requests. @@ -515,7 +547,7 @@ N-Factor Authentication ----------------------- Multiple login stages can be combined to create N-factor authentication during login. -This can be achieved by responding with the ``'next'`` login type on completion of a +This can be achieved by responding with the ``next`` login type on completion of a previous login stage:: { @@ -523,7 +555,7 @@ previous login stage:: } If a home server implements N-factor authentication, it MUST respond with all -``'stages'`` when initially queried for their login requirements:: +``stages`` when initially queried for their login requirements:: { "type": "<1st login type>", @@ -592,19 +624,19 @@ can also be performed. There are three main kinds of communication that occur between home servers: - * Queries + - Queries These are single request/response interactions between a given pair of servers, initiated by one side sending an HTTP request to obtain some information, and responded by the other. They are not persisted and contain no long-term significant history. They simply request a snapshot state at the instant the query is made. - * EDUs - Ephemeral Data Units + - EDUs - Ephemeral Data Units These are notifications of events that are pushed from one home server to another. They are not persisted and contain no long-term significant history, nor does the receiving home server have to reply to them. - * PDUs - Persisted Data Units + - PDUs - Persisted Data Units These are notifications of events that are broadcast from one home server to any others that are interested in the same "context" (namely, a Room ID). They are persisted to long-term storage and form the record of history for @@ -629,6 +661,8 @@ milliseconds) generated by its origin server, an origin and destination server name, a list of "previous IDs", and a list of PDUs - the actual message payload that the Transaction carries. +:: + {"transaction_id":"916d630ea616342b42e98a3be0b74113", "ts":1404835423000, "origin":"red", @@ -660,6 +694,8 @@ sent them), and a nested content field containing the actual event content. [[TODO(paul): Update this structure so that 'pdu_id' is a two-element [origin,ref] pair like the prev_pdus are]] +:: + {"pdu_id":"a4ecee13e2accdadf56c1025af232176", "context":"#example.green", "origin":"green", @@ -686,6 +722,8 @@ PDUs fall into two main categories: those that deliver Events, and those that synchronise State. For PDUs that relate to State synchronisation, additional keys exist to support this: +:: + {..., "is_state":true, "state_key":TODO @@ -704,6 +742,8 @@ EDUs, by comparison to PDUs, do not have an ID, a context, or a list of "previous" IDs. The only mandatory fields for these are the type, origin and destination home server names, and the actual nested content. +:: + {"edu_type":"m.presence", "origin":"blue", "destination":"orange", -- cgit 1.4.1 From d1bf659ed73622cab36dde1883f96aa41e8d840b Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 29 Aug 2014 10:30:14 +0100 Subject: Redo architecture diagram. Reword parts of federation. Formatting fixes and tweaks. --- docs/specification.rst | 178 +++++++++++++++++++++++++++---------------------- 1 file changed, 97 insertions(+), 81 deletions(-) (limited to 'docs') diff --git a/docs/specification.rst b/docs/specification.rst index 35fbd4dd22..d650683efc 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -28,6 +28,9 @@ Architecture - Client is an end-user (web app, mobile app) which uses C-S APIs to talk to the home server. A given client is typically responsible for a single user. +- A single user is represented by a User ID, scoped to the home server which allocated the account. + User IDs MUST have @ prefix; looks like @foo:domain - domain indicates the user's home + server. - Home server provides C-S APIs and has the ability to federate with other HSes. Typically responsible for N clients. - Federation's purpose is to share content between interested HSes; no SPOF. @@ -46,10 +49,16 @@ participants in that room will receive the message. Rooms are uniquely identified via a room ID. There is exactly one room ID for each room. Each room can also have an alias. Each room can have many aliases. -:: +- Room IDs MUST have ! prefix; looks like !foo:domain - domain is simply for namespacing, + the room does NOT reside on any one domain. NOT human readable. + +- Room Aliases MUST have # prefix; looks like #foo:domain - domain indicates where this + alias can be mapped to a room ID. Key point: human readable / friendly. + +- Aliases can be queried on the domain they specify, which will return a room ID if a + mapping exists. These mappings can change. - How events flow in rooms - ======================== +:: { @alice:matrix.org } { @bob:domain.com } | ^ @@ -59,25 +68,24 @@ room can also have an alias. Each room can have many aliases. Content: { JSON object } Content: { JSON object } | | V | - +------------------+ +------------------+ - | Home Server | | Home Server | - | matrix.org |<-------Federation--------->| domain.com | - +------------------+ +------------------+ - Room ID: !qporfwt:matrix.org Room ID: !qporfwt:matrix.org - Servers: matrix.org, domain.com Servers: matrix.org, domain.com - Members: Members: - - @alice:matrix.org - @alice:matrix.org - - @bob:domain.com - @bob:domain.com - + +------------------+ +------------------+ + | Home Server | | Home Server | + | matrix.org |<-------Federation------->| domain.com | + +------------------+ +------------------+ + | ................................. | + |______| Shared State |_______| + | Room ID: !qporfwt:matrix.org | + | Servers: matrix.org, domain.com | + | Members: | + | - @alice:matrix.org | + | - @bob:domain.com | + |.................................| + +- Federation's goal is to maintain the shared state. Don't need FULL state in order + to be a part of a room. +- Introduce the DAG. +- Events are wrapped in PDUs. -- Room IDs MUST have ! prefix; looks like !foo:domain - domain is simply for namespacing, - the room does NOT reside on domain. NOT human readable. -- Room Aliases MUST have # prefix; looks like #foo:domain - domain indicates where this - alias can be mapped to a room ID. Key point: human readable / friendly. -- User IDs MUST have @ prefix; looks like @foo:domain - domain indicates the user's home - server. -- Aliases can be queried on the domain they specify, which will return a room ID if a - mapping exists. These mappings can change. Identity -------- @@ -103,7 +111,7 @@ Receiving live updates on a client Rooms ===== -- How are they created? +- How are they created? PDU anchor point: "root of the tree". - Adding / removing aliases. - Invite/join dance - State and non-state data (+extensibility) @@ -167,15 +175,15 @@ below: - ``body`` : "string" - The alt text of the image, or some kind of content description for accessibility e.g. "image attachment". -ImageInfo: - Information about an image:: + 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)", - } + { + "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)", + } ``m.audio`` Required keys: @@ -186,15 +194,14 @@ ImageInfo: - ``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:: -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), - } + { + "mimetype" : "string (e.g. audio/aac)", + "size" : integer (size of audio in bytes), + "duration" : integer (duration of audio in milliseconds), + } ``m.video`` Required keys: @@ -205,18 +212,18 @@ AudioInfo: - ``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:: + VideoInfo: + Information about a video:: - { - "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) - } + { + "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) + } ``m.location`` Required keys: @@ -624,61 +631,62 @@ can also be performed. There are three main kinds of communication that occur between home servers: - - Queries +:Queries: These are single request/response interactions between a given pair of - servers, initiated by one side sending an HTTP request to obtain some + servers, initiated by one side sending an HTTP GET request to obtain some information, and responded by the other. They are not persisted and contain no long-term significant history. They simply request a snapshot state at the instant the query is made. - - EDUs - Ephemeral Data Units +:Ephemeral Data Units (EDUs): These are notifications of events that are pushed from one home server to another. They are not persisted and contain no long-term significant history, nor does the receiving home server have to reply to them. - - PDUs - Persisted Data Units +:Persisted Data Units (PDUs): These are notifications of events that are broadcast from one home server to any others that are interested in the same "context" (namely, a Room ID). They are persisted to long-term storage and form the record of history for that context. -Where Queries are presented directly across the HTTP connection as GET requests -to specific URLs, EDUs and PDUs are further wrapped in an envelope called a -Transaction, which is transferred from the origin to the destination home server -using a PUT request. +EDUs and PDUs are further wrapped in an envelope called a Transaction, which is +transferred from the origin to the destination home server using an HTTP PUT request. -Transactions and EDUs/PDUs --------------------------- +Transactions +------------ The transfer of EDUs and PDUs between home servers is performed by an exchange -of Transaction messages, which are encoded as JSON objects with a dict as the -top-level element, passed over an HTTP PUT request. A Transaction is meaningful -only to the pair of home servers that exchanged it; they are not globally- -meaningful. +of Transaction messages, which are encoded as JSON objects, passed over an +HTTP PUT request. A Transaction is meaningful only to the pair of home servers that +exchanged it; they are not globally-meaningful. -Each transaction has an opaque ID and timestamp (UNIX epoch time in -milliseconds) generated by its origin server, an origin and destination server -name, a list of "previous IDs", and a list of PDUs - the actual message payload -that the Transaction carries. +Each transaction has: + - An opaque transaction ID. + - A timestamp (UNIX epoch time in milliseconds) generated by its origin server. + - An origin and destination server name. + - A list of "previous IDs". + - A list of PDUs and EDUs - the actual message payload that the Transaction carries. :: - {"transaction_id":"916d630ea616342b42e98a3be0b74113", + { + "transaction_id":"916d630ea616342b42e98a3be0b74113", "ts":1404835423000, "origin":"red", "destination":"blue", "prev_ids":["e1da392e61898be4d2009b9fecce5325"], "pdus":[...], - "edus":[...]} + "edus":[...] + } -The "previous IDs" field will contain a list of previous transaction IDs that -the origin server has sent to this destination. Its purpose is to act as a +The ``prev_ids`` field contains a list of previous transaction IDs that +the ``origin`` server has sent to this ``destination``. Its purpose is to act as a sequence checking mechanism - the destination server can check whether it has successfully received that Transaction, or ask for a retransmission if not. -The "pdus" field of a transaction is a list, containing zero or more PDUs.[*] -Each PDU is itself a dict containing a number of keys, the exact details of -which will vary depending on the type of PDU. Similarly, the "edus" field is +The ``pdus`` field of a transaction is a list, containing zero or more PDUs.[*] +Each PDU is itself a JSON object containing a number of keys, the exact details of +which will vary depending on the type of PDU. Similarly, the ``edus`` field is another list containing the EDUs. This key may be entirely absent if there are no EDUs to transfer. @@ -687,27 +695,35 @@ receiving an "empty" transaction, as this is useful for informing peers of other transaction IDs they should be aware of. This effectively acts as a push mechanism to encourage peers to continue to replicate content.) -All PDUs have an ID, a context, a declaration of their type, a list of other PDU -IDs that have been seen recently on that context (regardless of which origin -sent them), and a nested content field containing the actual event content. +PDUs and EDUs +------------- + +All PDUs have: + - An ID + - A context + - A declaration of their type + - A list of other PDU IDs that have been seen recently on that context (regardless of which origin + sent them) [[TODO(paul): Update this structure so that 'pdu_id' is a two-element [origin,ref] pair like the prev_pdus are]] :: - {"pdu_id":"a4ecee13e2accdadf56c1025af232176", + { + "pdu_id":"a4ecee13e2accdadf56c1025af232176", "context":"#example.green", "origin":"green", "ts":1404838188000, "pdu_type":"m.text", "prev_pdus":[["blue","99d16afbc857975916f1d73e49e52b65"]], "content":... - "is_state":false} + "is_state":false + } -In contrast to the transaction layer, it is important to note that the prev_pdus +In contrast to Transactions, it is important to note that the ``prev_pdus`` field of a PDU refers to PDUs that any origin server has sent, rather than -previous IDs that this origin has sent. This list may refer to other PDUs sent +previous IDs that this ``origin`` has sent. This list may refer to other PDUs sent by the same origin as the current one, or other origins. Because of the distributed nature of participants in a Matrix conversation, it -- cgit 1.4.1 From ac56ac67cc19339abd56400aed86779ec2e2d76f Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 29 Aug 2014 11:41:48 +0100 Subject: Expand architecture section to introduce room IDs, room aliases, user IDs, events and federation. --- docs/specification.rst | 101 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 68 insertions(+), 33 deletions(-) (limited to 'docs') diff --git a/docs/specification.rst b/docs/specification.rst index d650683efc..c1559c886c 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -9,7 +9,9 @@ TODO(Introduction) : Matthew Architecture ============ -- Sending a message from A to B + +Clients transmit data to other clients through home servers (HSes). Clients do not communicate with each +other directly. :: @@ -26,39 +28,42 @@ Architecture | |<--------( HTTP )-----------| | +------------------+ Federation +------------------+ -- Client is an end-user (web app, mobile app) which uses C-S APIs to talk to the home server. - A given client is typically responsible for a single user. -- A single user is represented by a User ID, scoped to the home server which allocated the account. - User IDs MUST have @ prefix; looks like @foo:domain - domain indicates the user's home - server. -- Home server provides C-S APIs and has the ability to federate with other HSes. - Typically responsible for N clients. -- Federation's purpose is to share content between interested HSes; no SPOF. -- Events are actions within the system. Typically each action (e.g. sending a message) - correlates with exactly one event. Each event has a ``type`` string. -- ``type`` values SHOULD be namespaced according to standard Java package naming conventions, - with a ``.`` delimiter e.g. ``com.example.myapp.event`` -- Events are typically send in the context of a room. +A "Client" is an end-user, typically a human using a web application or mobile app. Clients use the +"Client-to-Server" (C-S) API to communicate with their home server. A single Client is usually +responsible for a single user account. A user account is represented by their "User ID". This ID is +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. + + +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 +sharing of data between two or more home servers. + +Data in Matrix is encapsulated in an "Event". An event is an action within the system. Typically each +action (e.g. sending a message) correlates with exactly one event. Each event has a ``type`` which is +used to differentiate different kinds of data. ``type`` values SHOULD be namespaced according to standard +Java package naming conventions, e.g. ``com.example.myapp.event``. Events are usually sent in the context +of a "Room". Room structure -------------- -A room is a conceptual place where users can send and receive messages. Rooms -can be created, joined and left. Messages are sent to a room, and all -participants in that room will receive the message. Rooms are uniquely -identified via a room ID. There is exactly one room ID for each room. Each -room can also have an alias. Each room can have many aliases. +A room is a conceptual place where users can send and receive events. Rooms +can be created, joined and left. Events are sent to a room, and all +participants in that room will receive the event. Rooms are uniquely +identified via a "Room ID", which look like:: -- Room IDs MUST have ! prefix; looks like !foo:domain - domain is simply for namespacing, - the room does NOT reside on any one domain. NOT human readable. + !opaque_id:domain -- Room Aliases MUST have # prefix; looks like #foo:domain - domain indicates where this - alias can be mapped to a room ID. Key point: human readable / friendly. +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. -- Aliases can be queried on the domain they specify, which will return a room ID if a - mapping exists. These mappings can change. - -:: +The following diagram shows an ``m.room.message`` event being sent in the room +``!qporfwt:matrix.org``:: { @alice:matrix.org } { @bob:domain.com } | ^ @@ -73,18 +78,48 @@ room can also have an alias. Each room can have many aliases. | matrix.org |<-------Federation------->| domain.com | +------------------+ +------------------+ | ................................. | - |______| Shared State |_______| - | Room ID: !qporfwt:matrix.org | + |______| Partially Shared State |_______| + | Room ID: !qporfwt:matrix.org | | Servers: matrix.org, domain.com | | Members: | | - @alice:matrix.org | | - @bob:domain.com | |.................................| -- Federation's goal is to maintain the shared state. Don't need FULL state in order - to be a part of a room. -- Introduce the DAG. -- Events are wrapped in PDUs. +Federation maintains shared state between multiple home servers, such that when an event is +sent to a room, the home server knows where to forward the event on to, and how to process +the event. Home servers do not need to have completely shared state in order to participate +in a room. State is scoped to a single room, and federation ensures that all home servers +have the information they need, even if that means the home server has to request more +information from another home server before processing the event. + +Room Aliases +------------ + +Each room can also have multiple "Room Aliases", which looks like:: + + #room_alias:domain + +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. + +:: + + GET + #matrix:domain.com !aaabaa:matrix.org + | ^ + | | + _______V____________________|____ + | domain.com | + | Mappings: | + | #matrix >> !aaabaa:matrix.org | + | #golf >> !wfeiofh:sport.com | + | #bike >> !4rguxf:matrix.org | + |________________________________| Identity -- cgit 1.4.1 From 898dde881217d00d39a5d7a0e5d20dad6821b344 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 29 Aug 2014 13:50:25 +0100 Subject: Flesh out API standards. --- docs/specification.rst | 85 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/specification.rst b/docs/specification.rst index c1559c886c..d4a01a3fc2 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -132,10 +132,87 @@ 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": "" + } + +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. + +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." + } Receiving live updates on a client ---------------------------------- -- cgit 1.4.1 From 339dd3dc6c44d84e869e435ca6e1520ccc587680 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 29 Aug 2014 14:12:12 +0100 Subject: Update API swagger JSON to default to POSTs not PUTs when describing the API. --- docs/client-server/swagger_matrix/rooms | 80 ++++++++------------------------- 1 file changed, 19 insertions(+), 61 deletions(-) (limited to 'docs') diff --git a/docs/client-server/swagger_matrix/rooms b/docs/client-server/swagger_matrix/rooms index bb49ec5a6a..1c32d135aa 100644 --- a/docs/client-server/swagger_matrix/rooms +++ b/docs/client-server/swagger_matrix/rooms @@ -14,12 +14,12 @@ }, "apis": [ { - "path": "/rooms/{roomId}/send/{eventType}/{txnId}", + "path": "/rooms/{roomId}/send/{eventType}", "operations": [ { - "method": "PUT", + "method": "POST", "summary": "Send a generic non-state event to this room.", - "notes": "This operation can also be done as a POST to /rooms/{roomId}/send/{eventType}", + "notes": "This operation can also be done as a PUT by suffixing /{txnId}.", "type": "EventId", "nickname": "send_non_state_event", "consumes": [ @@ -46,13 +46,6 @@ "required": true, "type": "string", "paramType": "path" - }, - { - "name": "txnId", - "description": "A client transaction ID to ensure idempotency. This can only be omitted if the HTTP method becomes a POST.", - "required": true, - "type": "string", - "paramType": "path" } ] } @@ -104,12 +97,12 @@ ] }, { - "path": "/rooms/{roomId}/send/m.room.message/{txnId}", + "path": "/rooms/{roomId}/send/m.room.message", "operations": [ { - "method": "PUT", + "method": "POST", "summary": "Send a message in this room.", - "notes": "This operation can also be done as a POST to /rooms/{roomId}/send/m.room.message", + "notes": "This operation can also be done as a PUT by suffixing /{txnId}.", "type": "EventId", "nickname": "send_message", "consumes": [ @@ -129,13 +122,6 @@ "required": true, "type": "string", "paramType": "path" - }, - { - "name": "txnId", - "description": "A client transaction ID to ensure idempotency. This can only be omitted if the HTTP method becomes a POST.", - "required": true, - "type": "string", - "paramType": "path" } ] } @@ -195,12 +181,12 @@ ] }, { - "path": "/rooms/{roomId}/send/m.room.message.feedback/{txnId}", + "path": "/rooms/{roomId}/send/m.room.message.feedback", "operations": [ { - "method": "PUT", + "method": "POST", "summary": "Send feedback to a message.", - "notes": "This operation can also be done as a POST to /rooms/{roomId}/send/m.room.message.feedback", + "notes": "This operation can also be done as a PUT by suffixing /{txnId}.", "type": "EventId", "nickname": "send_feedback", "consumes": [ @@ -220,13 +206,6 @@ "required": true, "type": "string", "paramType": "path" - }, - { - "name": "txnId", - "description": "A client transaction ID to ensure idempotency. This can only be omitted if the HTTP method becomes a POST.", - "required": true, - "type": "string", - "paramType": "path" } ], "responseMessages": [ @@ -239,12 +218,12 @@ ] }, { - "path": "/rooms/{roomId}/invite/{txnId}", + "path": "/rooms/{roomId}/invite", "operations": [ { - "method": "PUT", + "method": "POST", "summary": "Invite a user to this room.", - "notes": "This operation can also be done as a POST to /rooms/{roomId}/invite", + "notes": "This operation can also be done as a PUT by suffixing /{txnId}.", "type": "void", "nickname": "invite", "consumes": [ @@ -258,13 +237,6 @@ "type": "string", "paramType": "path" }, - { - "name": "txnId", - "description": "A client transaction ID for this PUT to ensure idempotency. This can only be omitted if the HTTP method becomes a POST. ", - "required": false, - "type": "string", - "paramType": "path" - }, { "name": "body", "description": "The user to invite.", @@ -277,12 +249,12 @@ ] }, { - "path": "/rooms/{roomId}/join/{txnId}", + "path": "/rooms/{roomId}/join", "operations": [ { - "method": "PUT", + "method": "POST", "summary": "Join this room.", - "notes": "This operation can also be done as a POST to /rooms/{roomId}/join", + "notes": "This operation can also be done as a PUT by suffixing /{txnId}.", "type": "void", "nickname": "join_room", "consumes": [ @@ -295,25 +267,18 @@ "required": true, "type": "string", "paramType": "path" - }, - { - "name": "txnId", - "description": "A client transaction ID for this PUT to ensure idempotency. This can only be omitted if the HTTP method becomes a POST. ", - "required": false, - "type": "string", - "paramType": "path" } ] } ] }, { - "path": "/rooms/{roomId}/leave/{txnId}", + "path": "/rooms/{roomId}/leave", "operations": [ { - "method": "PUT", + "method": "POST", "summary": "Leave this room.", - "notes": "This operation can also be done as a POST to /rooms/{roomId}/leave", + "notes": "This operation can also be done as a PUT by suffixing /{txnId}.", "type": "void", "nickname": "leave", "consumes": [ @@ -326,13 +291,6 @@ "required": true, "type": "string", "paramType": "path" - }, - { - "name": "txnId", - "description": "A client transaction ID for this PUT to ensure idempotency. This can only be omitted if the HTTP method becomes a POST. ", - "required": false, - "type": "string", - "paramType": "path" } ] } @@ -476,7 +434,7 @@ "parameters": [ { "name": "body", - "description": "The desired configuration for the room.", + "description": "The desired configuration for the room. This operation can also be done as a PUT by suffixing /{txnId}.", "required": true, "type": "RoomConfig", "paramType": "body" -- cgit 1.4.1