move matrix-generic content to new matrix-doc git project
parent
81b956c70d
commit
e1170d4edb
|
@ -0,0 +1,6 @@
|
||||||
|
All matrix-generic documentation now lives in its own project at
|
||||||
|
|
||||||
|
github.com/matrix-org/matrix-doc.git
|
||||||
|
|
||||||
|
Only Synapse implementation-specific documentation lives here now
|
||||||
|
(together with some older stuff will be shortly migrated over to matrix-doc)
|
|
@ -1,637 +0,0 @@
|
||||||
.. 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
|
|
|
@ -1,53 +0,0 @@
|
||||||
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.
|
|
|
@ -1,79 +0,0 @@
|
||||||
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.
|
|
|
@ -1,43 +0,0 @@
|
||||||
===================
|
|
||||||
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.
|
|
|
@ -1,89 +0,0 @@
|
||||||
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?
|
|
|
@ -1,30 +0,0 @@
|
||||||
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
|
@ -1,51 +0,0 @@
|
||||||
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.
|
|
Loading…
Reference in New Issue