mirror of https://github.com/watcha-fr/synapse
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