move matrix-generic documentation from synapse/docs into new matrix-doc project
parent
06723d7465
commit
556e3f8a71
@ -0,0 +1,53 @@
|
|||||||
|
Definitions
|
||||||
|
===========
|
||||||
|
|
||||||
|
# *Event* -- A JSON object that represents a piece of information to be
|
||||||
|
distributed to the the room. The object includes a payload and metadata,
|
||||||
|
including a `type` used to indicate what the payload is for and how to process
|
||||||
|
them. It also includes one or more references to previous events.
|
||||||
|
|
||||||
|
# *Event graph* -- Events and their references to previous events form a
|
||||||
|
directed acyclic graph. All events must be a descendant of the first event in a
|
||||||
|
room, except for a few special circumstances.
|
||||||
|
|
||||||
|
# *State event* -- A state event is an event that has a non-null string valued
|
||||||
|
`state_key` field. It may also include a `prev_state` key referencing exactly
|
||||||
|
one state event with the same type and state key, in the same event graph.
|
||||||
|
|
||||||
|
# *State tree* -- A state tree is a tree formed by a collection of state events
|
||||||
|
that have the same type and state key (all in the same event graph.
|
||||||
|
|
||||||
|
# *State resolution algorithm* -- An algorithm that takes a state tree as input
|
||||||
|
and selects a single leaf node.
|
||||||
|
|
||||||
|
# *Current state event* -- The leaf node of a given state tree that has been
|
||||||
|
selected by the state resolution algorithm.
|
||||||
|
|
||||||
|
# *Room state* / *state dictionary* / *current state* -- A mapping of the pair
|
||||||
|
(event type, state key) to the current state event for that pair.
|
||||||
|
|
||||||
|
# *Room* -- An event graph and its associated state dictionary. An event is in
|
||||||
|
the room if it is part of the event graph.
|
||||||
|
|
||||||
|
# *Topological ordering* -- The partial ordering that can be extracted from the
|
||||||
|
event graph due to it being a DAG.
|
||||||
|
|
||||||
|
(The state definitions are purposely slightly ill-defined, since if we allow
|
||||||
|
deleting events we might end up with multiple state trees for a given event
|
||||||
|
type and state key pair.)
|
||||||
|
|
||||||
|
Federation specific
|
||||||
|
-------------------
|
||||||
|
# *(Persistent data unit) PDU* -- An encoding of an event for distribution of
|
||||||
|
the server to server protocol.
|
||||||
|
|
||||||
|
# *(Ephemeral data unit) EDU* -- A piece of information that is sent between
|
||||||
|
servers and doesn't encode an event.
|
||||||
|
|
||||||
|
Client specific
|
||||||
|
---------------
|
||||||
|
# *Child events* -- Events that reference a single event in the same room
|
||||||
|
independently of the event graph.
|
||||||
|
|
||||||
|
# *Collapsed events* -- Events that have all child events that reference it
|
||||||
|
included in the JSON object.
|
@ -0,0 +1,79 @@
|
|||||||
|
This document outlines the format for human-readable IDs within matrix.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
UTF-8 is quickly becoming the standard character encoding set on the web. As
|
||||||
|
such, Matrix requires that all strings MUST be encoded as UTF-8. However,
|
||||||
|
using Unicode as the character set for human-readable IDs is troublesome. There
|
||||||
|
are many different characters which appear identical to each other, but would
|
||||||
|
identify different users. In addition, there are non-printable characters which
|
||||||
|
cannot be rendered by the end-user. This opens up a security vulnerability with
|
||||||
|
phishing/spoofing of IDs, commonly known as a homograph attack.
|
||||||
|
|
||||||
|
Web browers encountered this problem when International Domain Names were
|
||||||
|
introduced. A variety of checks were put in place in order to protect users. If
|
||||||
|
an address failed the check, the raw punycode would be displayed to disambiguate
|
||||||
|
the address. Similar checks are performed by home servers in Matrix. However,
|
||||||
|
Matrix does not use punycode representations, and so does not show raw punycode
|
||||||
|
on a failed check. Instead, home servers must outright reject these misleading
|
||||||
|
IDs.
|
||||||
|
|
||||||
|
Types of human-readable IDs
|
||||||
|
---------------------------
|
||||||
|
There are two main human-readable IDs in question:
|
||||||
|
|
||||||
|
- Room aliases
|
||||||
|
- User IDs
|
||||||
|
|
||||||
|
Room aliases look like ``#localpart:domain``. These aliases point to opaque
|
||||||
|
non human-readable room IDs. These pointers can change, so there is already an
|
||||||
|
issue present with the same ID pointing to a different destination at a later
|
||||||
|
date.
|
||||||
|
|
||||||
|
User IDs look like ``@localpart:domain``. These represent actual end-users, and
|
||||||
|
unlike room aliases, there is no layer of indirection. This presents a much
|
||||||
|
greater concern with homograph attacks.
|
||||||
|
|
||||||
|
Checks
|
||||||
|
------
|
||||||
|
- Similar to web browsers.
|
||||||
|
- blacklisted chars (e.g. non-printable characters)
|
||||||
|
- mix of language sets from 'preferred' language not allowed.
|
||||||
|
- Language sets from CLDR dataset.
|
||||||
|
- Treated in segments (localpart, domain)
|
||||||
|
- Additional restrictions for ease of processing IDs.
|
||||||
|
- Room alias localparts MUST NOT have ``#`` or ``:``.
|
||||||
|
- User ID localparts MUST NOT have ``@`` or ``:``.
|
||||||
|
|
||||||
|
Rejecting
|
||||||
|
---------
|
||||||
|
- Home servers MUST reject room aliases which do not pass the check, both on
|
||||||
|
GETs and PUTs.
|
||||||
|
- Home servers MUST reject user ID localparts which do not pass the check, both
|
||||||
|
on creation and on events.
|
||||||
|
- Any home server whose domain does not pass this check, MUST use their punycode
|
||||||
|
domain name instead of the IDN, to prevent other home servers rejecting you.
|
||||||
|
- Error code is ``M_FAILED_HUMAN_ID_CHECK``. (generic enough for both failing
|
||||||
|
due to homograph attacks, and failing due to including ``:`` s, etc)
|
||||||
|
- Error message MAY go into further information about which characters were
|
||||||
|
rejected and why.
|
||||||
|
- Error message SHOULD contain a ``failed_keys`` key which contains an array
|
||||||
|
of strings which represent the keys which failed the check e.g::
|
||||||
|
|
||||||
|
failed_keys: [ user_id, room_alias ]
|
||||||
|
|
||||||
|
Other considerations
|
||||||
|
--------------------
|
||||||
|
- Basic security: Informational key on the event attached by HS to say "unsafe
|
||||||
|
ID". Problem: clients can just ignore it, and since it will appear only very
|
||||||
|
rarely, easy to forget when implementing clients.
|
||||||
|
- Moderate security: Requires client handshake. Forces clients to implement
|
||||||
|
a check, else they cannot communicate with the misleading ID. However, this is
|
||||||
|
extra overhead in both client implementations and round-trips.
|
||||||
|
- High security: Outright rejection of the ID at the point of creation /
|
||||||
|
receiving event. Point of creation rejection is preferable to avoid the ID
|
||||||
|
entering the system in the first place. However, malicious HSes can just allow
|
||||||
|
the ID. Hence, other home servers must reject them if they see them in events.
|
||||||
|
Client never sees the problem ID, provided the HS is correctly implemented.
|
||||||
|
- High security decided; client doesn't need to worry about it, no additional
|
||||||
|
protocol complexity aside from rejection of an event.
|
@ -0,0 +1,51 @@
|
|||||||
|
State Resolution
|
||||||
|
================
|
||||||
|
This section describes why we need state resolution and how it works.
|
||||||
|
|
||||||
|
|
||||||
|
Motivation
|
||||||
|
-----------
|
||||||
|
We want to be able to associate some shared state with rooms, e.g. a room name
|
||||||
|
or members list. This is done by having a current state dictionary that maps
|
||||||
|
from the pair event type and state key to an event.
|
||||||
|
|
||||||
|
However, since the servers involved in the room are distributed we need to be
|
||||||
|
able to handle the case when two (or more) servers try and update the state at
|
||||||
|
the same time. This is done via the state resolution algorithm.
|
||||||
|
|
||||||
|
|
||||||
|
State Tree
|
||||||
|
------------
|
||||||
|
State events contain a reference to the state it is trying to replace. These
|
||||||
|
relations form a tree where the current state is one of the leaf nodes.
|
||||||
|
|
||||||
|
Note that state events are events, and so are part of the PDU graph. Thus we
|
||||||
|
can be sure that (modulo the internet being particularly broken) we will see
|
||||||
|
all state events eventually.
|
||||||
|
|
||||||
|
|
||||||
|
Algorithm requirements
|
||||||
|
----------------------
|
||||||
|
We want the algorithm to have the following properties:
|
||||||
|
- Since we aren't guaranteed what order we receive state events in, except that
|
||||||
|
we see parents before children, the state resolution algorithm must not depend
|
||||||
|
on the order and must always come to the same result.
|
||||||
|
- If we receive a state event whose parent is the current state, then the
|
||||||
|
algorithm will select it.
|
||||||
|
- The algorithm does not depend on internal state, ensuring all servers should
|
||||||
|
come to the same decision.
|
||||||
|
|
||||||
|
These three properties mean it is enough to keep track of the current state and
|
||||||
|
compare it with any new proposed state, rather than having to keep track of all
|
||||||
|
the leafs of the tree and recomputing across the entire state tree.
|
||||||
|
|
||||||
|
|
||||||
|
Current Implementation
|
||||||
|
----------------------
|
||||||
|
The current implementation works as follows: Upon receipt of a newly proposed
|
||||||
|
state change we first find the common ancestor. Then we take the maximum
|
||||||
|
across each branch of the users' power levels, if one is higher then it is
|
||||||
|
selected as the current state. Otherwise, we check if one chain is longer than
|
||||||
|
the other, if so we choose that one. If that also fails, then we concatenate
|
||||||
|
all the pdu ids and take a SHA1 hash and compare them to select a common
|
||||||
|
ancestor.
|
@ -0,0 +1,43 @@
|
|||||||
|
===================
|
||||||
|
Documentation Style
|
||||||
|
===================
|
||||||
|
|
||||||
|
A brief single sentence to describe what this file contains; in this case a
|
||||||
|
description of the style to write documentation in.
|
||||||
|
|
||||||
|
|
||||||
|
Sections
|
||||||
|
========
|
||||||
|
|
||||||
|
Each section should be separated from the others by two blank lines. Headings
|
||||||
|
should be underlined using a row of equals signs (===). Paragraphs should be
|
||||||
|
separated by a single blank line, and wrap to no further than 80 columns.
|
||||||
|
|
||||||
|
[[TODO(username): if you want to leave some unanswered questions, notes for
|
||||||
|
further consideration, or other kinds of comment, use a TODO section. Make sure
|
||||||
|
to notate it with your name so we know who to ask about it!]]
|
||||||
|
|
||||||
|
Subsections
|
||||||
|
-----------
|
||||||
|
|
||||||
|
If required, subsections can use a row of dashes to underline their header. A
|
||||||
|
single blank line between subsections of a single section.
|
||||||
|
|
||||||
|
|
||||||
|
Bullet Lists
|
||||||
|
============
|
||||||
|
|
||||||
|
* Bullet lists can use asterisks with a single space either side.
|
||||||
|
|
||||||
|
* Another blank line between list elements.
|
||||||
|
|
||||||
|
|
||||||
|
Definition Lists
|
||||||
|
================
|
||||||
|
|
||||||
|
Terms:
|
||||||
|
Start in the first column, ending with a colon
|
||||||
|
|
||||||
|
Definitions:
|
||||||
|
Take a two space indent, following immediately from the term without a blank
|
||||||
|
line before it, but having a blank line afterwards.
|
@ -0,0 +1,89 @@
|
|||||||
|
In C-S API > Registration/Login:
|
||||||
|
|
||||||
|
Captcha-based
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
:Type:
|
||||||
|
``m.login.recaptcha``
|
||||||
|
:Description:
|
||||||
|
Login is supported by responding to a captcha, in this case Google's
|
||||||
|
Recaptcha.
|
||||||
|
|
||||||
|
To respond to this type, reply with::
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "m.login.recaptcha",
|
||||||
|
"challenge": "<challenge token>",
|
||||||
|
"response": "<user-entered text>"
|
||||||
|
}
|
||||||
|
|
||||||
|
The Recaptcha parameters can be obtained in Javascript by calling::
|
||||||
|
|
||||||
|
Recaptcha.get_challenge();
|
||||||
|
Recaptcha.get_response();
|
||||||
|
|
||||||
|
The home server MUST respond with either new credentials, the next stage of the
|
||||||
|
login process, or a standard error response.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
In Events:
|
||||||
|
|
||||||
|
Common event fields
|
||||||
|
-------------------
|
||||||
|
All events MUST have the following fields:
|
||||||
|
|
||||||
|
``event_id``
|
||||||
|
Type:
|
||||||
|
String.
|
||||||
|
Description:
|
||||||
|
Represents the globally unique ID for this event.
|
||||||
|
|
||||||
|
``type``
|
||||||
|
Type:
|
||||||
|
String.
|
||||||
|
Description:
|
||||||
|
Contains the event type, e.g. ``m.room.message``
|
||||||
|
|
||||||
|
``content``
|
||||||
|
Type:
|
||||||
|
JSON Object.
|
||||||
|
Description:
|
||||||
|
Contains the content of the event. When interacting with the REST API, this is the HTTP body.
|
||||||
|
|
||||||
|
``room_id``
|
||||||
|
Type:
|
||||||
|
String.
|
||||||
|
Description:
|
||||||
|
Contains the ID of the room associated with this event.
|
||||||
|
|
||||||
|
``user_id``
|
||||||
|
Type:
|
||||||
|
String.
|
||||||
|
Description:
|
||||||
|
Contains the fully-qualified ID of the user who *sent* this event.
|
||||||
|
|
||||||
|
State events have the additional fields:
|
||||||
|
|
||||||
|
``state_key``
|
||||||
|
Type:
|
||||||
|
String.
|
||||||
|
Description:
|
||||||
|
Contains the state key for this state event. If there is no state key for this state event, this
|
||||||
|
will be an empty string. The presence of ``state_key`` makes this event a state event.
|
||||||
|
|
||||||
|
``required_power_level``
|
||||||
|
Type:
|
||||||
|
Integer.
|
||||||
|
Description:
|
||||||
|
Contains the minimum power level a user must have before they can update this event.
|
||||||
|
|
||||||
|
``prev_content``
|
||||||
|
Type:
|
||||||
|
JSON Object.
|
||||||
|
Description:
|
||||||
|
Optional. Contains the previous ``content`` for this event. If there is no previous content, this
|
||||||
|
key will be missing.
|
||||||
|
|
||||||
|
.. TODO-spec
|
||||||
|
How do "age" and "ts" fit in to all this? Which do we expose?
|
@ -0,0 +1,30 @@
|
|||||||
|
Matrix Specification NOTHAVEs
|
||||||
|
=============================
|
||||||
|
|
||||||
|
This document contains sections of the main specification that have been
|
||||||
|
temporarily removed, because they specify intentions or aspirations that have
|
||||||
|
in no way yet been implemented. Rather than outright-deleting them, they have
|
||||||
|
been moved here so as to stand as an initial version for such time as they
|
||||||
|
become extant.
|
||||||
|
|
||||||
|
|
||||||
|
Presence
|
||||||
|
========
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,637 @@
|
|||||||
|
.. TODO kegan
|
||||||
|
Room config (specifically: message history,
|
||||||
|
public rooms). /register seems super simplistic compared to /login, maybe it
|
||||||
|
would be better if /register used the same technique as /login? /register should
|
||||||
|
be "user" not "user_id".
|
||||||
|
|
||||||
|
|
||||||
|
How to use the client-server API
|
||||||
|
================================
|
||||||
|
|
||||||
|
This guide focuses on how the client-server APIs *provided by the reference
|
||||||
|
home server* can be used. Since this is specific to a home server
|
||||||
|
implementation, there may be variations in relation to registering/logging in
|
||||||
|
which are not covered in extensive detail in this guide.
|
||||||
|
|
||||||
|
If you haven't already, get a home server up and running on
|
||||||
|
``http://localhost:8008``.
|
||||||
|
|
||||||
|
|
||||||
|
Accounts
|
||||||
|
========
|
||||||
|
Before you can send and receive messages, you must **register** for an account.
|
||||||
|
If you already have an account, you must **login** into it.
|
||||||
|
|
||||||
|
`Try out the fiddle`__
|
||||||
|
|
||||||
|
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/register_login
|
||||||
|
|
||||||
|
Registration
|
||||||
|
------------
|
||||||
|
The aim of registration is to get a user ID and access token which you will need
|
||||||
|
when accessing other APIs::
|
||||||
|
|
||||||
|
curl -XPOST -d '{"user":"example", "password":"wordpass", "type":"m.login.password"}' "http://localhost:8008/_matrix/client/api/v1/register"
|
||||||
|
|
||||||
|
{
|
||||||
|
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.AqdSzFmFYrLrTmteXc",
|
||||||
|
"home_server": "localhost",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
}
|
||||||
|
|
||||||
|
NB: If a ``user`` is not specified, one will be randomly generated for you.
|
||||||
|
If you do not specify a ``password``, you will be unable to login to the account
|
||||||
|
if you forget the ``access_token``.
|
||||||
|
|
||||||
|
Implementation note: The matrix specification does not enforce how users
|
||||||
|
register with a server. It just specifies the URL path and absolute minimum
|
||||||
|
keys. The reference home server uses a username/password to authenticate user,
|
||||||
|
but other home servers may use different methods. This is why you need to
|
||||||
|
specify the ``type`` of method.
|
||||||
|
|
||||||
|
Login
|
||||||
|
-----
|
||||||
|
The aim when logging in is to get an access token for your existing user ID::
|
||||||
|
|
||||||
|
curl -XGET "http://localhost:8008/_matrix/client/api/v1/login"
|
||||||
|
|
||||||
|
{
|
||||||
|
"flows": [
|
||||||
|
{
|
||||||
|
"type": "m.login.password"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "http://localhost:8008/_matrix/client/api/v1/login"
|
||||||
|
|
||||||
|
{
|
||||||
|
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd",
|
||||||
|
"home_server": "localhost",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
}
|
||||||
|
|
||||||
|
Implementation note: Different home servers may implement different methods for
|
||||||
|
logging in to an existing account. In order to check that you know how to login
|
||||||
|
to this home server, you must perform a ``GET`` first and make sure you
|
||||||
|
recognise the login type. If you do not know how to login, you can
|
||||||
|
``GET /login/fallback`` which will return a basic webpage which you can use to
|
||||||
|
login. The reference home server implementation support username/password login,
|
||||||
|
but other home servers may support different login methods (e.g. OAuth2).
|
||||||
|
|
||||||
|
|
||||||
|
Communicating
|
||||||
|
=============
|
||||||
|
|
||||||
|
In order to communicate with another user, you must **create a room** with that
|
||||||
|
user and **send a message** to that room.
|
||||||
|
|
||||||
|
`Try out the fiddle`__
|
||||||
|
|
||||||
|
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/create_room_send_msg
|
||||||
|
|
||||||
|
Creating a room
|
||||||
|
---------------
|
||||||
|
If you want to send a message to someone, you have to be in a room with them. To
|
||||||
|
create a room::
|
||||||
|
|
||||||
|
curl -XPOST -d '{"room_alias_name":"tutorial"}' "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token=YOUR_ACCESS_TOKEN"
|
||||||
|
|
||||||
|
{
|
||||||
|
"room_alias": "#tutorial:localhost",
|
||||||
|
"room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
|
||||||
|
}
|
||||||
|
|
||||||
|
The "room alias" is a human-readable string which can be shared with other users
|
||||||
|
so they can join a room, rather than the room ID which is a randomly generated
|
||||||
|
string. You can have multiple room aliases per room.
|
||||||
|
|
||||||
|
.. TODO(kegan)
|
||||||
|
How to add/remove aliases from an existing room.
|
||||||
|
|
||||||
|
|
||||||
|
Sending messages
|
||||||
|
----------------
|
||||||
|
You can now send messages to this room::
|
||||||
|
|
||||||
|
curl -XPOST -d '{"msgtype":"m.text", "body":"hello"}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/send/m.room.message?access_token=YOUR_ACCESS_TOKEN"
|
||||||
|
|
||||||
|
{
|
||||||
|
"event_id": "YUwRidLecu"
|
||||||
|
}
|
||||||
|
|
||||||
|
The event ID returned is a unique ID which identifies this message.
|
||||||
|
|
||||||
|
NB: There are no limitations to the types of messages which can be exchanged.
|
||||||
|
The only requirement is that ``"msgtype"`` is specified. The Matrix
|
||||||
|
specification outlines the following standard types: ``m.text``, ``m.image``,
|
||||||
|
``m.audio``, ``m.video``, ``m.location``, ``m.emote``. See the specification for
|
||||||
|
more information on these types.
|
||||||
|
|
||||||
|
Users and rooms
|
||||||
|
===============
|
||||||
|
|
||||||
|
Each room can be configured to allow or disallow certain rules. In particular,
|
||||||
|
these rules may specify if you require an **invitation** from someone already in
|
||||||
|
the room in order to **join the room**. In addition, you may also be able to
|
||||||
|
join a room **via a room alias** if one was set up.
|
||||||
|
|
||||||
|
`Try out the fiddle`__
|
||||||
|
|
||||||
|
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/room_memberships
|
||||||
|
|
||||||
|
Inviting a user to a room
|
||||||
|
-------------------------
|
||||||
|
You can directly invite a user to a room like so::
|
||||||
|
|
||||||
|
curl -XPOST -d '{"user_id":"@myfriend:localhost"}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/invite?access_token=YOUR_ACCESS_TOKEN"
|
||||||
|
|
||||||
|
This informs ``@myfriend:localhost`` of the room ID
|
||||||
|
``!CvcvRuDYDzTOzfKKgh:localhost`` and allows them to join the room.
|
||||||
|
|
||||||
|
Joining a room via an invite
|
||||||
|
----------------------------
|
||||||
|
If you receive an invite, you can join the room::
|
||||||
|
|
||||||
|
curl -XPOST -d '{}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/join?access_token=YOUR_ACCESS_TOKEN"
|
||||||
|
|
||||||
|
NB: Only the person invited (``@myfriend:localhost``) can change the membership
|
||||||
|
state to ``"join"``. Repeatedly joining a room does nothing.
|
||||||
|
|
||||||
|
Joining a room via an alias
|
||||||
|
---------------------------
|
||||||
|
Alternatively, if you know the room alias for this room and the room config
|
||||||
|
allows it, you can directly join a room via the alias::
|
||||||
|
|
||||||
|
curl -XPOST -d '{}' "http://localhost:8008/_matrix/client/api/v1/join/%23tutorial%3Alocalhost?access_token=YOUR_ACCESS_TOKEN"
|
||||||
|
|
||||||
|
{
|
||||||
|
"room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
|
||||||
|
}
|
||||||
|
|
||||||
|
You will need to use the room ID when sending messages, not the room alias.
|
||||||
|
|
||||||
|
NB: If the room is configured to be an invite-only room, you will still require
|
||||||
|
an invite in order to join the room even though you know the room alias. As a
|
||||||
|
result, it is more common to see a room alias in relation to a public room,
|
||||||
|
which do not require invitations.
|
||||||
|
|
||||||
|
Getting events
|
||||||
|
==============
|
||||||
|
An event is some interesting piece of data that a client may be interested in.
|
||||||
|
It can be a message in a room, a room invite, etc. There are many different ways
|
||||||
|
of getting events, depending on what the client already knows.
|
||||||
|
|
||||||
|
`Try out the fiddle`__
|
||||||
|
|
||||||
|
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/event_stream
|
||||||
|
|
||||||
|
Getting all state
|
||||||
|
-----------------
|
||||||
|
If the client doesn't know any information on the rooms the user is
|
||||||
|
invited/joined on, they can get all the user's state for all rooms::
|
||||||
|
|
||||||
|
curl -XGET "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=YOUR_ACCESS_TOKEN"
|
||||||
|
|
||||||
|
{
|
||||||
|
"end": "s39_18_0",
|
||||||
|
"presence": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"last_active_ago": 1061436,
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
"type": "m.presence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rooms": [
|
||||||
|
{
|
||||||
|
"membership": "join",
|
||||||
|
"messages": {
|
||||||
|
"chunk": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"@example:localhost": 10,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"event_id": "wAumPSTsWF",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.power_levels",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"join_rule": "public"
|
||||||
|
},
|
||||||
|
"event_id": "jrLVqKHKiI",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.join_rules",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"level": 10
|
||||||
|
},
|
||||||
|
"event_id": "WpmTgsNWUZ",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.add_state_level",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"level": 0
|
||||||
|
},
|
||||||
|
"event_id": "qUMBJyKsTQ",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.send_event_level",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"ban_level": 5,
|
||||||
|
"kick_level": 5
|
||||||
|
},
|
||||||
|
"event_id": "YAaDmKvoUW",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.ops_levels",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"avatar_url": null,
|
||||||
|
"displayname": null,
|
||||||
|
"membership": "join"
|
||||||
|
},
|
||||||
|
"event_id": "RJbPMtCutf",
|
||||||
|
"membership": "join",
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "@example:localhost",
|
||||||
|
"ts": 1409665586730,
|
||||||
|
"type": "m.room.member",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "hello",
|
||||||
|
"hsob_ts": 1409665660439,
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"event_id": "YUwRidLecu",
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"ts": 1409665660439,
|
||||||
|
"type": "m.room.message",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"membership": "invite"
|
||||||
|
},
|
||||||
|
"event_id": "YjNuBKnPsb",
|
||||||
|
"membership": "invite",
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "@myfriend:localhost",
|
||||||
|
"ts": 1409666426819,
|
||||||
|
"type": "m.room.member",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"avatar_url": null,
|
||||||
|
"displayname": null,
|
||||||
|
"membership": "join",
|
||||||
|
"prev": "join"
|
||||||
|
},
|
||||||
|
"event_id": "KWwdDjNZnm",
|
||||||
|
"membership": "join",
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "@example:localhost",
|
||||||
|
"ts": 1409666551582,
|
||||||
|
"type": "m.room.member",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"avatar_url": null,
|
||||||
|
"displayname": null,
|
||||||
|
"membership": "join"
|
||||||
|
},
|
||||||
|
"event_id": "JFLVteSvQc",
|
||||||
|
"membership": "join",
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "@example:localhost",
|
||||||
|
"ts": 1409666587265,
|
||||||
|
"type": "m.room.member",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": "s39_18_0",
|
||||||
|
"start": "t1-11_18_0"
|
||||||
|
},
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"creator": "@example:localhost"
|
||||||
|
},
|
||||||
|
"event_id": "dMUoqVTZca",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.create",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"@example:localhost": 10,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"event_id": "wAumPSTsWF",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.power_levels",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"join_rule": "public"
|
||||||
|
},
|
||||||
|
"event_id": "jrLVqKHKiI",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.join_rules",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"level": 10
|
||||||
|
},
|
||||||
|
"event_id": "WpmTgsNWUZ",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.add_state_level",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"level": 0
|
||||||
|
},
|
||||||
|
"event_id": "qUMBJyKsTQ",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.send_event_level",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"ban_level": 5,
|
||||||
|
"kick_level": 5
|
||||||
|
},
|
||||||
|
"event_id": "YAaDmKvoUW",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.ops_levels",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"membership": "invite"
|
||||||
|
},
|
||||||
|
"event_id": "YjNuBKnPsb",
|
||||||
|
"membership": "invite",
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "@myfriend:localhost",
|
||||||
|
"ts": 1409666426819,
|
||||||
|
"type": "m.room.member",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"avatar_url": null,
|
||||||
|
"displayname": null,
|
||||||
|
"membership": "join"
|
||||||
|
},
|
||||||
|
"event_id": "JFLVteSvQc",
|
||||||
|
"membership": "join",
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "@example:localhost",
|
||||||
|
"ts": 1409666587265,
|
||||||
|
"type": "m.room.member",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
This returns all the room information the user is invited/joined on, as well as
|
||||||
|
all of the presences relevant for these rooms. This can be a LOT of data. You
|
||||||
|
may just want the most recent event for each room. This can be achieved by
|
||||||
|
applying query parameters to ``limit`` this request::
|
||||||
|
|
||||||
|
curl -XGET "http://localhost:8008/_matrix/client/api/v1/initialSync?limit=1&access_token=YOUR_ACCESS_TOKEN"
|
||||||
|
|
||||||
|
{
|
||||||
|
"end": "s39_18_0",
|
||||||
|
"presence": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"last_active_ago": 1279484,
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
"type": "m.presence"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rooms": [
|
||||||
|
{
|
||||||
|
"membership": "join",
|
||||||
|
"messages": {
|
||||||
|
"chunk": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"avatar_url": null,
|
||||||
|
"displayname": null,
|
||||||
|
"membership": "join"
|
||||||
|
},
|
||||||
|
"event_id": "JFLVteSvQc",
|
||||||
|
"membership": "join",
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "@example:localhost",
|
||||||
|
"ts": 1409666587265,
|
||||||
|
"type": "m.room.member",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": "s39_18_0",
|
||||||
|
"start": "t10-30_18_0"
|
||||||
|
},
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"creator": "@example:localhost"
|
||||||
|
},
|
||||||
|
"event_id": "dMUoqVTZca",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.create",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"@example:localhost": 10,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"event_id": "wAumPSTsWF",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.power_levels",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"join_rule": "public"
|
||||||
|
},
|
||||||
|
"event_id": "jrLVqKHKiI",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.join_rules",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"level": 10
|
||||||
|
},
|
||||||
|
"event_id": "WpmTgsNWUZ",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.add_state_level",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"level": 0
|
||||||
|
},
|
||||||
|
"event_id": "qUMBJyKsTQ",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.send_event_level",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"ban_level": 5,
|
||||||
|
"kick_level": 5
|
||||||
|
},
|
||||||
|
"event_id": "YAaDmKvoUW",
|
||||||
|
"required_power_level": 10,
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "",
|
||||||
|
"ts": 1409665585188,
|
||||||
|
"type": "m.room.ops_levels",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"membership": "invite"
|
||||||
|
},
|
||||||
|
"event_id": "YjNuBKnPsb",
|
||||||
|
"membership": "invite",
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "@myfriend:localhost",
|
||||||
|
"ts": 1409666426819,
|
||||||
|
"type": "m.room.member",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"avatar_url": null,
|
||||||
|
"displayname": null,
|
||||||
|
"membership": "join"
|
||||||
|
},
|
||||||
|
"event_id": "JFLVteSvQc",
|
||||||
|
"membership": "join",
|
||||||
|
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
||||||
|
"state_key": "@example:localhost",
|
||||||
|
"ts": 1409666587265,
|
||||||
|
"type": "m.room.member",
|
||||||
|
"user_id": "@example:localhost"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Getting live state
|
||||||
|
------------------
|
||||||
|
Once you know which rooms the client has previously interacted with, you need to
|
||||||
|
listen for incoming events. This can be done like so::
|
||||||
|
|
||||||
|
curl -XGET "http://localhost:8008/_matrix/client/api/v1/events?access_token=YOUR_ACCESS_TOKEN"
|
||||||
|
|
||||||
|
{
|
||||||
|
"chunk": [],
|
||||||
|
"end": "s39_18_0",
|
||||||
|
"start": "s39_18_0"
|
||||||
|
}
|
||||||
|
|
||||||
|
This will block waiting for an incoming event, timing out after several seconds.
|
||||||
|
Even if there are no new events (as in the example above), there will be some
|
||||||
|
pagination stream response keys. The client should make subsequent requests
|
||||||
|
using the value of the ``"end"`` key (in this case ``s39_18_0``) as the ``from``
|
||||||
|
query parameter e.g. ``http://localhost:8008/_matrix/client/api/v1/events?access
|
||||||
|
_token=YOUR_ACCESS_TOKEN&from=s39_18_0``. This value should be stored so when the
|
||||||
|
client reopens your app after a period of inactivity, you can resume from where
|
||||||
|
you got up to in the event stream. If it has been a long period of inactivity,
|
||||||
|
there may be LOTS of events waiting for the user. In this case, you may wish to
|
||||||
|
get all state instead and then resume getting live state from a newer end token.
|
||||||
|
|
||||||
|
NB: The timeout can be changed by adding a ``timeout`` query parameter, which is
|
||||||
|
in milliseconds. A timeout of 0 will not block.
|
||||||
|
|
||||||
|
|
||||||
|
Example application
|
||||||
|
-------------------
|
||||||
|
The following example demonstrates registration and login, live event streaming,
|
||||||
|
creating and joining rooms, sending messages, getting member lists and getting
|
||||||
|
historical messages for a room. This covers most functionality of a messaging
|
||||||
|
application.
|
||||||
|
|
||||||
|
`Try out the fiddle`__
|
||||||
|
|
||||||
|
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/example_app
|
Loading…
Reference in New Issue