Merge branch 'master' of github.com:matrix-org/matrix-doc

pull/977/head
Kegan Dougal 10 years ago
commit 74b7769c08

@ -0,0 +1,244 @@
.. TODO
Sometimes application services need to create rooms (e.g. when lazy loading
from room aliases). Created rooms need to have a user that created them, so
federation works (as it relies on an entry existing in m.room.member). We should
be able to add metadata to m.room.member to state that this user is an application
service, a virtual user, etc.
Application Services
====================
Overview
========
Application services provide a way of implementing custom serverside functionality
on top of Matrix without the complexity of implementing the full federation API.
By acting as a trusted service logically located behind an existing homeserver,
Application services are decoupled from:
* Signing or validating federated traffic or conversation history
* Validating authorisation constraints on federated traffic
* Managing routing or retry schemes to the rest of the Matrix federation
As such, developers can focus entirely on implementing application logic rather
than being concerned with the details of managing Matrix federation.
Features available to application services include:
* Privileged subscription to any events available to the homeserver
* Synthesising virtual users
* Synthesising virtual rooms
* Injecting message history for virtual rooms
Features not provided by application services include:
* Intercepting and filtering/modifying message or behaviour within a room
(this is a job for a Policy Server, as it requires a single logical focal
point for messages in order to consistently apply the custom business logic)
Example use cases for application services include:
* Exposing existing communication services in Matrix
* Gateways to/from standards-based protocols (SIP, XMPP, IRC, RCS (MSRP), SIMPLE, Lync, etc)
* Gateways to/from closed services (e.g. WhatsApp)
* Gateways could be architected as:
* Act as a virtual client on the non-Matrix network
(e.g. connect as multiple virtual clients to an IRC or XMPP server)
* Act as a server on the non-Matrix network
(e.g. speak s2s XMPP federation, or IRC link protocol)
* Act as an application service on the non-Matrix network
(e.g. link up as IRC services, or an XMPP component)
* Exposing a non-Matrix client interface listener from the AS
(e.g. listen on port 6667 for IRC clients, or port 5222 for XMPP clients)
* Bridging existing APIs into Matrix
* e.g. SMS/MMS aggregator APIs
* Domain-specific APIs such as SABRE
* Integrating more exotic content into Matrix
* e.g. MIDI<->Matrix gateway/bridge
* 3D world <-> Matrix bridge
* Application services:
* Search engines (e.g. elasticsearch search indices)
* Notification systems (e.g. send custom pushes for various hooks)
* VoIP Conference services
* Text-to-speech and Speech-to-text services
* Signal processing
* IVR
* Server-machine translation
* Censorship service
* Multi-User Gaming (Dungeons etc)
* Other "constrained worlds" (e.g. 3D geometry representations)
* applying physics to a 3D world on the serverside
* (applying gravity and friction and air resistance... collision detection)
* domain-specific merge conflict resolution of events
* Payment style transactional usecases with transactional guarantees
Architecture Outline
====================
The application service registers with its host homeserver to offer its services.
In the registration process, the AS provides:
* Credentials to identify itself as an approved application service for that HS
* Details of the namespaces of users and rooms the AS is acting on behalf of and
"subscribing to"
* Namespaces are defined as a list of regexps against which to match room aliases,
room IDs, and user IDs. Regexps give the flexibility to say, sub-domain MSISDN
ranges per AS, whereas a blunt prefix string does not. These namespaces are further
configured by setting whether they are ``exclusive`` or not. An exclusive namespace
prevents entities other than the aforementioned AS from creating/editing/deleting
entries within that namespace. This does not affect the visibility/readability of
entries within that namespace (e.g. it doesn't prevent users joining exclusive
aliases, or ASes from listening to exclusive aliases, but does prevent both users
and ASes from creating/editing/deleting aliases within that namespace).
* There is overlap between selecting events via the csv2 Filter API and subscribing
to events here - perhaps subscription involves passing a filter token into the
registration API.
* A URL base for receiving requests from the HS (as the AS is a server,
implementers expect to receive data via inbound requests rather than
long-poll outbound requests)
On HS handling events to unknown users:
* If the HS receives an event for an unknown user who is in the namespace delegated to
the AS, then the HS queries the AS for the profile of that user. If the AS
confirms the existence of that user (from its perspective), then the HS
creates an account to represent the virtual user.
* The namespace of virtual user accounts should conform to a structure like
``@.irc.freenode.Arathorn:matrix.org``. This lets Matrix users communicate with
foreign users who are not yet mapped into Matrix via 3PID mappings or through
an existing non-virtual Matrix user by trying to talk to them via a gateway.
* The AS can alternatively preprovision virtual users using the existing CS API
rather than lazy-loading them in this manner.
* The AS may want to link the matrix ID of the sender through to their 3PID in
the remote ecosystem. E.g. a message sent from ``@matthew:matrix.org`` may wish
to originate from Arathorn on irc.freenode.net in the case of an IRC bridge.
It's left as an AS implementation detail as to how the user should authorise
the AS to act on its behalf.
On HS handling events to unknown rooms:
* If the HS receives an invite to an unknown room which is in the namespace
delegated to the AS, then the HS queries the AS for the existence of that room.
If the AS confirms its existence (from its perspective), then the HS creates
the room.
* The initial state of the room may be populated by the AS by querying an
initialSync API (probably a subset of the CS initialSync API, to reuse the
same pattern for the equivalent function). As messages have to be signed
from the point of ``m.room.create``, we will not be able to back-populate
arbitrary history for rooms which are lazy-created in this manner, and instead
have to chose the amount of history to be synchronised into the AS as a one-off.
* If exposing arbitrary history is required, then:
* either: the room history must be preemptively provisioned in the HS by the AS via
the CS API (TODO: meaning the CS API needs to support massaged
timestamps), resulting in conversation history being replicated between
the HS and the source store.
* or: the HS must delegate conversation storage entirely to the
AS using a Storage API (not defined here) which allows the existing
conversation store to back the HS, complete with all necessary Matrix
metadata (e.g. hashes, signatures, federation DAG, etc). This obviously
increases the burden of implementing an AS considerably, but is the only
option if the implementer wants to avoid duplicating conversation history
between the external data source and the HS.
On HS handling events to existing users and rooms:
* If the HS receives an event for a user or room that already exists (either
provisioned by the AS or by normal client interactions), then the message
is handled as normal.
* Events in the namespaces of rooms and users that the AS has subscribed to
are pushed to the AS using the same pattern as the federation API (without
any of the encryption or federation metadata). This serves precisely the
same purpose as the CS event stream and has the same data flow semantics
(and indeed an AS implementer could chose to use the CS event stream instead)
* Events are linearised to avoid the AS having to handle the complexity of
linearisation, and because if linearisation is good enough for CS, it
should be good enough for AS. Should the AS require non-linearised events
from Matrix, it should implement the federation API rather than the AS API
instead.
* HS->AS event pushes are retried for reliability with sequence numbers
(or logical timestamping?) to preserve the linearisation order and ensure
a reliable event stream.
* Clustered HSes must linearise just as they do for the CS API. Clustered
ASes must loadbalance the inbound stream across the cluster as required.
On AS relaying events from unknown-to-HS users:
* AS injects the event to the HS using the CS API, irrespective of whether the
target user or room is known to the HS or not. If the HS doesn't recognise
the target it goes through the same lazy-load provisioning as per above.
* The reason for not using a subset of the federation API here is because it
allows AS developers to reuse existing CS SDKs and benefit from the more
meaningful error handling of the CS API. The sending user ID must be
explicitly specified, as it cannot be inferred from the access_token, which
will be the same for all AS requests.
* TODO: or do we maintain a separate ``access_token`` mapping? It seems like
unnecessary overhead for the AS developer; easier to just use a single
privileged ``access_token`` and just track which ``user_id`` is emitting events?
* If the AS is spoofing the identity of a real (not virtual) matrix user,
we should actually let them log themselves in via OAuth2 to give permission
to the AS to act on their behalf.
* We can't auth gatewayed virtual users from 3rd party systems who are being
relayed into Matrix, as the relaying is happening whether the user likes it
or not. Therefore we do need to be able to spoof sender ID for virtual users.
On AS relaying events in unknown-to-HS rooms:
* See above.
On AS publishing aliases for virtual rooms:
* AS uses the normal alias management API to preemptively create/delete public
directory entries for aliases for virtual rooms provided by the AS.
* In order to create these aliases, the underlying room ID must also exist, so
at least the ``m.room.create`` of that room must also be prepopulated. It seems
sensible to prepopulate the required initial state and history of the room to
avoid a two-phase prepopulation process.
On unregistering the AS from the HS:
* An AS must tell the HS when it is going offline in order to stop receiving
requests from the HS. It does this by hitting an API on the HS.
AS Visibility:
* If an AS needs to sniff events in a room in order to operate on them (e.g.
to act as a search engine) but not inject traffic into the room, it should
do so by subscribing to the relevant events without actually joining the room.
* If the AS needs to participate in the room as a virtual user (e.g. an IVR
service, or a bot, or a gatewayed virtual user), it should join the room
normally.
* There are rare instances where an AS may wish to participate in a room
(including inserting messages), but be hidden from the room list - e.g. a
conferencing server focus bot may wish to join many rooms as the focus and
both listen to VoIP setups and inject its own VoIP answers, without ever
being physically seen in the room. In this scenario, the user should set
its presence to 'invisible', a state that HSes should only allow AS-authed
users to set.
E2E Encryption
* The AS obviously has no visibility to E2E encrypted messages, unless it is
explicitly added to an encrypted room and participates in the group chat
itself.
Extensions to CS API
====================
* Ability to assert the identity of the virtual user for all methods.
* Ability to massage timestamps when prepopulating historical state and
messages of virtual rooms (either by overriding ``origin_server_ts`` (preferred) or
adding an ``as_ts`` which we expect clients to honour)
* Ability to delete aliases (including from the directory) as well as create them.

@ -0,0 +1,572 @@
.. TODO
Sometimes application services need to create rooms (e.g. when lazy loading
from room aliases). Created rooms need to have a user that created them, so
federation works (as it relies on an entry existing in m.room.member). We
should be able to add metadata to m.room.member to state that this user is an
application service, a virtual user, etc.
Application Services HTTP API
=============================
.. contents:: Table of Contents
.. sectnum::
Application Service -> Home Server
----------------------------------
This contains home server APIs which are used by the application service.
Registration API ``[Draft]``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This API registers the application service with its host homeserver to offer its
services.
Inputs:
- Credentials (e.g. some kind of string token)
- Namespace[users]
- Namespace[room aliases]
- URL base to receive inbound comms
Output:
- The credentials the HS will use to query the AS with in return. (e.g. some
kind of string token)
Side effects:
- The HS will start delivering events to the URL base specified if this 200s.
API called when:
- The application service wants to register with a brand new home server.
Notes:
- An application service can state whether they should be the only ones who
can manage a specified namespace. This is referred to as an "exclusive"
namespace. An exclusive namespace prevents humans and other application
services from creating/deleting entities in that namespace. Typically,
exclusive namespaces are used when the rooms represent real rooms on
another service (e.g. IRC). Non-exclusive namespaces are used when the
application service is merely augmenting the room itself (e.g. providing
logging or searching facilities).
- Namespaces are represented by POSIX extended regular expressions in JSON.
They look like::
users: [
{
"exclusive": true,
"regex": "@irc\.freenode\.net/.*"
}
]
::
POST /register
Request format
{
url: "https://my.application.service.com/matrix/",
as_token: "some_AS_token",
namespaces: {
users: [
{
"exclusive": true,
"regex": "@irc\.freenode\.net/.*"
}
],
aliases: [
{
"exclusive": true,
"regex": "#irc\.freenode\.net/.*"
}
],
rooms: [
{
"exclusive": true,
"regex": "!irc\.freenode\.net/.*"
}
]
}
}
Returns:
200 : Registration accepted.
400 : Namespaces do not conform to regex
401 : Credentials need to be supplied.
403 : AS credentials rejected.
200 OK response format
{
hs_token: "string"
}
Unregister API ``[Draft]``
~~~~~~~~~~~~~~~~~~~~~~~~~~
This API unregisters a previously registered AS from the home server.
Inputs:
- AS token
Output:
- None.
Side effects:
- The HS will stop delivering events to the URL base specified for this AS if
this 200s.
API called when:
- The application service wants to stop receiving all events from the HS.
::
POST /unregister
Request format
{
as_token: "string"
}
Home Server -> Application Service
----------------------------------
This contains application service APIs which are used by the home server.
User Query ``[Draft]``
~~~~~~~~~~~~~~~~~~~~~~
This API is called by the HS to query the existence of a user on the Application
Service's namespace.
Inputs:
- User ID
- HS Credentials
Output:
- Whether the user exists.
Side effects:
- User is created on the HS by the AS via CS APIs during the processing of this request.
API called when:
- HS receives an event for an unknown user ID in the AS's namespace, e.g. an
invite event to a room.
Notes:
- When the AS receives this request, if the user exists, it must create the user via
the CS API.
- It can also set arbitrary information about the user (e.g. display name, join rooms, etc)
using the CS API.
- When this setup is complete, the AS should respond to the HS request. This means the AS
blocks the HS until the user is created.
- This is deemed more flexible than alternative methods (e.g. returning a JSON blob with the
user's display name and get the HS to provision the user).
Retry notes:
- The home server cannot respond to the client's request until the response to
this API is obtained from the AS.
- Recommended that home servers try a few times then time out, returning a
408 Request Timeout to the client.
::
GET /users/$user_id?access_token=$hs_token
Returns:
200 : User is recognised.
404 : User not found.
401 : Credentials need to be supplied.
403 : HS credentials rejected.
200 OK response format
{}
Room Alias Query ``[Draft]``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This API is called by the HS to query the existence of a room alias on the
Application Service's namespace.
Inputs:
- Room alias
- HS Credentials
Output:
- Whether the room exists.
Side effects:
- Room is created on the HS by the AS via CS APIs during the processing of
this request.
API called when:
- HS receives an event to join a room alias in the AS's namespace.
Notes:
- When the AS receives this request, if the room exists, it must create the room via
the CS API.
- It can also set arbitrary information about the room (e.g. name, topic, etc)
using the CS API.
- It can send messages as other users in order to populate scrollback.
- When this setup is complete, the AS should respond to the HS request. This means the AS
blocks the HS until the room is created and configured.
- This is deemed more flexible than alternative methods (e.g. returning an initial sync
style JSON blob and get the HS to provision the room). It also means that the AS knows
the room ID -> alias mapping.
Retry notes:
- The home server cannot respond to the client's request until the response to
this API is obtained from the AS.
- Recommended that home servers try a few times then time out, returning a
408 Request Timeout to the client.
::
GET /rooms/$room_alias?access_token=$hs_token
Returns:
200 : Room is recognised.
404 : Room not found.
401 : Credentials need to be supplied.
403 : HS credentials rejected.
200 OK response format
{}
Pushing ``[Draft]``
~~~~~~~~~~~~~~~~~~~
This API is called by the HS when the HS wants to push an event (or batch of
events) to the AS.
Inputs:
- HS Credentials
- Event(s) to give to the AS
- HS-generated transaction ID
Output:
- None.
Data flows:
::
Typical
HS ---> AS : Home server sends events with transaction ID T.
<--- : AS sends back 200 OK.
AS ACK Lost
HS ---> AS : Home server sends events with transaction ID T.
<-/- : AS 200 OK is lost.
HS ---> AS : Home server retries with the same transaction ID of T.
<--- : AS sends back 200 OK. If the AS had processed these events
already, it can NO-OP this request (and it knows if it is the same
events based on the transacton ID).
Retry notes:
- If the HS fails to pass on the events to the AS, it must retry the request.
- Since ASes by definition cannot alter the traffic being passed to it (unlike
say, a Policy Server), these requests can be done in parallel to general HS
processing; the HS doesn't need to block whilst doing this.
- Home servers should use exponential backoff as their retry algorithm.
- Home servers MUST NOT alter (e.g. add more) events they were going to
send within that transaction ID on retries, as the AS may have already
processed the events.
Ordering notes:
- The events sent to the AS should be linearised, as they are from the event
stream.
- The home server will need to maintain a queue of transactions to send to
the AS.
::
PUT /transactions/$transaction_id?access_token=$hs_token
Request format
{
events: [
...
]
}
Client-Server v2 API Extensions
-------------------------------
Identity assertion
~~~~~~~~~~~~~~~~~~
The client-server API infers the user ID from the ``access_token`` provided in
every request. It would be an annoying amount of book-keeping to maintain tokens
for every virtual user. It would be preferable if the application service could
use the CS API with its own ``as_token`` instead, and specify the virtual user
they wish to be acting on behalf of. For real users, this would require
additional permissions granting the AS permission to masquerade as a matrix user.
Inputs:
- Application service token (``access_token``)
Either:
- User ID in the AS namespace to act as.
Or:
- OAuth2 token of real user (which may end up being an access token)
Notes:
- This will apply on all aspects of the CS API, except for Account Management.
- The ``as_token`` is inserted into ``access_token`` which is usually where the
client token is. This is done on purpose to allow application services to
reuse client SDKs.
::
/path?access_token=$token&user_id=$userid
Query Parameters:
access_token: The application service token
user_id: The desired user ID to act as.
/path?access_token=$token&user_token=$token
Query Parameters:
access_token: The application service token
user_token: The token granted to the AS by the real user
Timestamp massaging
~~~~~~~~~~~~~~~~~~~
The application service may want to inject events at a certain time (reflecting
the time on the network they are tracking e.g. irc, xmpp). Application services
need to be able to adjust the ``origin_server_ts`` value to do this.
Inputs:
- Application service token (``as_token``)
- Desired timestamp
Notes:
- This will only apply when sending events.
::
/path?access_token=$token&ts=$timestamp
Query Parameters added to the send event APIs only:
access_token: The application service token
ts: The desired timestamp
Server admin style permissions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The home server needs to give the application service *full control* over its
namespace, both for users and for room aliases. This means that the AS should
be able to create/edit/delete any room alias in its namespace, as well as
create/delete any user in its namespace. No additional API changes need to be
made in order for control of room aliases to be granted to the AS. Creation of
users needs API changes in order to:
- Work around captchas.
- Have a 'passwordless' user.
This involves bypassing the registration flows entirely. This is achieved by
including the AS token on a ``/register`` request, along with a login type of
``m.login.application_service`` to set the desired user ID without a password.
::
/register?access_token=$as_token
Content:
{
type: "m.login.application_service",
user: "<desired user localpart in AS namespace>"
}
Application services which attempt to create users or aliases *outside* of
their defined namespaces will receive an error code ``M_EXCLUSIVE``. Similarly,
normal users who attempt to create users or alises *inside* an application
service-defined namespace will receive the same ``M_EXCLUSIVE`` error code.
ID conventions ``[Draft]``
--------------------------
.. NOTE::
- Giving HSes the freedom to namespace still feels like the Right Thing here.
- Exposing a public API provides the consistency which was the main complaint
against namespacing.
- This may have knock-on effects for the AS registration API. E.g. why don't
we let ASes specify the *URI* regex they want?
This concerns the well-defined conventions for mapping 3P network IDs to matrix
IDs, which we expect clients to be able to do by themselves.
User IDs
~~~~~~~~
Matrix users may wish to directly contact a virtual user, e.g. to send an email.
The URI format is a well-structured way to represent a number of different ID
types, including:
- MSISDNs (``tel``)
- Email addresses (``mailto``)
- IRC nicks (``irc`` - https://tools.ietf.org/html/draft-butcher-irc-url-04)
- XMPP (xep-0032)
- SIP URIs (RFC 3261)
As a result, virtual user IDs SHOULD relate to their URI counterpart. This
mapping from URI to user ID can be expressed in a number of ways:
- Expose a C-S API on the HS which takes URIs and responds with user IDs.
- Munge the URI with the user ID.
Exposing an API would allow HSes to internally map user IDs however they like,
at the cost of an extra round trip (of which the response can be cached).
Munging the URI would allow clients to apply the mapping locally, but would force
user X on service Y to always map to the same munged user ID. Considering the
exposed API could just be applying this munging, there is more flexibility if
an API is exposed.
::
GET /_matrix/app/v1/user?uri=$url_encoded_uri
Returns 200 OK:
{
user_id: <complete user ID on local HS>
}
Room Aliases
~~~~~~~~~~~~
We may want to expose some 3P network rooms so Matrix users can join them directly,
e.g. IRC rooms. We don't want to expose every 3P network room though, e.g. mailto,
tel. Rooms which are publicly accessible (e.g. IRC rooms) can be exposed as an alias by
the application service. Private rooms (e.g. sending an email to someone) should not
be exposed in this way, but should instead operate using normal invite/join semantics.
Therefore, the ID conventions discussed below are only valid for public rooms which
expose room aliases.
Matrix users may wish to join XMPP rooms (e.g. using XEP-0045) or IRC rooms. In both
cases, these rooms can be expressed as URIs. For consistency, these "room" URIs
SHOULD be mapped in the same way as "user" URIs.
::
GET /_matrix/app/v1/alias?uri=$url_encoded_uri
Returns 200 OK:
{
alias: <complete room alias on local HS>
}
Event fields
~~~~~~~~~~~~
We recommend that any gatewayed events should include an `external_url` field in
their content to provide a way for Matrix clients to link into the 'native'
client from which the event originated. For instance, this could contain the
message-ID for emails/nntp posts, or a link to a blog comment when gatewaying
blog comment traffic in & out of matrix
Examples
--------
.. NOTE::
- User/Alias namespaces are subject to change depending on ID conventions.
IRC
~~~
Pre-conditions:
- Server admin stores the AS token "T_a" on the home server.
- Home server has a token "T_h".
- Home server has the domain "hsdomain.com"
1. Application service registration
::
AS -> HS: Registers itself with the home server
POST /register
{
url: "https://someapp.com/matrix",
as_token: "T_a",
namespaces: {
users: [
{
"exclusive": true,
"regex": "@irc\.freenode\.net/.*"
}
],
aliases: [
{
"exclusive": true,
"regex": "#irc\.freenode\.net/.*"
}
]
}
}
Returns 200 OK:
{
hs_token: "T_h"
}
2. IRC user "Bob" says "hello?" on "#matrix" at timestamp 1421416883133:
::
- AS stores message as potential scrollback.
- Nothing happens as no Matrix users are in the room.
3. Matrix user "@alice:hsdomain.com" wants to join "#matrix":
::
User -> HS: Request to join "#irc.freenode.net/#matrix:hsdomain.com"
HS -> AS: Room Query "#irc.freenode.net/#matrix:hsdomain.com"
GET /rooms/%23irc.freenode.net%2F%23matrix%3Ahsdomain.com?access_token=T_h
[Starts blocking]
AS -> HS: Creates room. Gets room ID "!aasaasasa:hsdomain.com".
AS -> HS: Sets room name to "#matrix".
AS -> HS: Sends message as ""@irc.freenode.net/Bob:hsdomain.com"
PUT /rooms/%21aasaasasa%3Ahsdomain.com/send/m.room.message
?access_token=T_a
&user_id=%40irc.freenode.net%2FBob%3Ahsdomain.com
&ts=1421416883133
{
body: "hello?"
msgtype: "m.text"
}
HS -> AS: User Query "@irc.freenode.net/Bob:hsdomain.com"
GET /users/%40irc.freenode.net%2FBob%3Ahsdomain.com?access_token=T_h
[Starts blocking]
AS -> HS: Creates user using CS API extension.
POST /register?access_token=T_a
{
type: "m.login.application_service",
user: "irc.freenode.net/Bob"
}
AS -> HS: Set user display name to "Bob".
[Finishes blocking]
[Finished blocking]
- HS sends room information back to client.
4. @alice:hsdomain.com says "hi!" in this room:
::
User -> HS: Send message "hi!" in room !aasaasasa:hsdomain.com
- HS sends message.
- HS sees the room ID is in the AS namespace and pushes it to the AS.
HS -> AS: Push event
PUT /transactions/1?access_token=T_h
{
events: [
{
content: {
body: "hi!",
msgtype: "m.text"
},
origin_server_ts: <generated by hs>,
user_id: "@alice:hsdomain.com",
room_id: "!aasaasasa:hsdomain.com",
type: "m.room.message"
}
]
}
- AS passes this through to IRC.
5. IRC user "Bob" says "what's up?" on "#matrix" at timestamp 1421418084816:
::
IRC -> AS: "what's up?"
AS -> HS: Send message via CS API extension
PUT /rooms/%21aasaasasa%3Ahsdomain.com/send/m.room.message
?access_token=T_a
&user_id=%40irc.freenode.net%2FBob%3Ahsdomain.com
&ts=1421418084816
{
body: "what's up?"
msgtype: "m.text"
}
- HS modifies the user_id and origin_server_ts on the event and sends it.

@ -52,7 +52,8 @@ within a given rectangle.
Homeservers may generate thumbnails for content uploaded to remote
homeservers themselves or may rely on the remote homeserver to thumbnail
the content. Homeservers may return thumbnails of a different size to that
requested. However homeservers should provide extact matches where reasonable.
requested. However homeservers should provide exact matches where reasonable.
Homeservers must never upscale images.
Security
--------
@ -73,4 +74,4 @@ Clients may try to access a large number of remote files through a homeserver.
Homeservers should restrict the number and size of remote files that it caches.
Clients or remote homeservers may try to upload malicious files targeting
vunerabilities in either the homeserver thumbnailing or the client decoders.
vulnerabilities in either the homeserver thumbnailing or the client decoders.

@ -0,0 +1,48 @@
Gatewaying to the PSTN via Matrix Application Services
======================================================
Matrix Application Services (AS) provides a way for PSTN users to interact
with Matrix via an AS acting as a gateway. Each PSTN user is represented as a
virtual user on a specific homeserver maintained by the AS. Typically the AS
is provisioned on a well-known AS-supplier HS (e.g. matrix.openmarket.com) or
is a service provisioned on the user's local HS.
In either scenario, the AS maintains virtual users of form
@.tel.e164:homeserver. These are lazily created (as per the AS spec) when
matrix users try to contact a user id of form @.tel.*:homeserver, or when the
AS needs to inject traffic into the HS on behalf of the PSTN user. The reason
for these being a visible virtual user rather than an invisible user or an
invisible sniffing AS is because they do represent real physical 3rd party
endpoints in the PSTN, and need to be able to send return messages.
Communication with an actual PSTN user happens in a normal Matrix room, which
for 1:1 matrix<->pstn contact will typically store all conversation history
with that user. On first contact, the matrix user invites the virtual user
into the room (or vice versa). In the event of switching to another AS-enabled
HS, the matrix user would kick the old AS and invite the new one. In the event
of needing loadbalancing between two SMS gateways (for instance), the user
would set visibility flags (TODO: specify per-message ACLs, or use crypto to
only sign messages so they're visible to certain other users?) to adjust which
virtual AS users could see which messages in the room.
For group chat, one or more AS virtual users may be invited to a group chat,
where-upon they will relay all the traffic in that group chat through to their
PSTN counterpart (and vice versa). This behaviour requires no additional
functionality beyond that required to support 1:1 chat.
When contacting a user, Matrix clients should check whether a given E.164
number is already mapped to a real Matrix user by querying the identity
servers (or subscribing to identity updates for a given E.164 number. TODO: ID
server subscriptions). If the E.164 number has a validated mapping in the ID
server to a Matrix ID, then this target ID should be used instead of
contacting the virtual user.
It's likely that PSTN gateway ASes will need to charge the end-user for use of
the gateway. The AS must therefore track credit per matrix ID it interacts
with, and stop gatewaying as desired once credit is exhausted. The task of
extracting credit from the end-user and adding it to the AS is not covered by
the Matrix specification.
For SMS routing, options are:
* Terminate traffic only (from a shared shortcode originator)
* Two-way traffic via a VMN. To save allocating huge numbers of VMNs to Matrix users, the VMN can be allocated from a pool such that each {caller,callee} tuple is unique (but the caller number will only work from that specific callee).

@ -0,0 +1,291 @@
Push Notifications
==================
Pushers
-------
To receive any notification pokes at all, it is necessary to configure a
'pusher' on the Home Server that you wish to receive notifications from. There
is a single API endpoint for this::
POST $PREFIX/pushers/set
This takes a JSON object with the following keys:
pushkey
This is a unique identifier for this pusher. The value you should use for this
is the routing or destination address information for the notification, for
example, the APNS token for APNS or the Registration ID for GCM. If your
notification client has no such concept, use any unique identifier.
kind
The kind of pusher to configure. 'http' makes a pusher that sends HTTP pokes.
null deletes the pusher.
profile_tag
This is a string that determines what set of device rules will be matched when
evaluating push rules for this pusher. It is an arbitrary string. Multiple
devices maybe use the same profile_tag. It is advised that when an app's
data is copied or restored to a different device, this value remain the same.
Client apps should offer ways to change the profile_tag, optionally copying
rules from the old profile tag.
app_id
appId is a reverse-DNS style identifier for the application. It is recommended
that this end with the platform, such that different platform versions get
different app identifiers. Max length, 64 chars.
app_display_name
A string that will allow the user to identify what application owns this
pusher.
device_display_name
A string that will allow the user to identify what device owns this pusher.
lang
The preferred language for receiving notifications (eg, 'en' or 'en-US')
data
A dictionary of information for the pusher implementation itself. For HTTP
pushers, this must contain a 'url' key which is a string of the URL that
should be used to send notifications.
If the pusher was created successfully, an empty JSON dictionary is returned.
Push Rules
----------
Home Servers have an interface to configure what events trigger notifications.
This behaviour is configured through 'Push Rules'. Push Rules come in a variety
of different kinds and each kind of rule has an associated priority. The
different kinds of rule, in descending order of priority, are:
Override Rules
The highest priority rules are user-configured overrides.
Content Rules
These configure behaviour for (unencrypted) messages that match certain
patterns. Content rules take one parameter, 'pattern', that gives the pattern
to match against. This is treated in the same way as pattern for event_match
conditions, below.
Room Rules
These change the behaviour of all messages to a given room. The rule_id of a
room rule is always the ID of the room that it affects.
Sender
These rules configure notification behaviour for messages from a specific,
named Matrix user ID. The rule_id of Sender rules is always the Matrix user
ID of the user whose messages theyt apply to.
Underride
These are identical to override rules, but have a lower priority than content,
room and sender rules.
In addition, each kind of rule may be either global or device-specific. Device
specific rules only affect delivery of notifications via pushers with a matching
profile_tag. All device-specific rules are higher priority than all global
rules. Thusly, the full list of rule kinds, in descending priority order, is as
follows:
* Device-specific Override
* Device-specific Content
* Device-specific Room
* Device-specific Sender
* Device-specific Underride
* Global Override
* Global Content
* Global Room
* Global Sender
* Global Underride
For some kinds of rule, rules of the same kind also have an ordering with
respect to one another. The kinds that do not are room and sender rules where
the rules are mutually exclusive by definition and therefore an ordering would
be redundant. Actions for the highest priority rule and only that rule apply
(for example, a set_tweak action in a lower priority rule will not apply if a
higher priority rule matches, even if that rule does not specify any tweaks).
Rules also have an identifier, rule_id, which is a string. The rule_id is
unique within the kind of rule and scope: rule_ids need not be unique between
rules of the same kind on different devices.
A home server may also have server default rules of each kind and in each scope.
Server default rules are lower priority than user-defined rules in each scope.
Server defined rules do not have a rule_id except when it is necessary to derive
the function of the rule (ie. in room and sender rules). Server default rules
have an attribute, "default" set to true.
Push Rules: Actions:
--------------------
All rules have an associated list of 'actions'. An action affects if and how a
notification is delievered for a matching event. This standard defines the
following actions, although if Home servers wish to support more, they are free
to do so:
notify
This causes each matching event to generate a notification.
dont_notify
Prevents this event from generating a notification
coalesce
This enables notifications for matching events but activates Home Server
specific behaviour to intelligently coalesce multiple events into a single
notification. Not all Home Servers may support this. Those that do not should
treat it as the 'notify' action.
set_tweak
Sets an entry in the 'tweaks' dictionary key that is sent in the notification
poke. This takes the form of a dictionary with a 'set_tweak' key whose value
is the name of the tweak to set. It must also have a 'value' key which is
the value to which it should be set.
Actions that have no parameters are represented as a string. Otherwise, they are
represented as a dictionary with a key equal to their name and other keys as
their parameters, eg. { "set_tweak": "sound", "value": "default" }
Push Rule Actions: Tweaks
-------------------------
The 'set_tweak' key action is used to add an entry to the 'tweaks' dictionary
that is sent in the notification poke. The following tweaks are defined:
sound
A sound to be played when this notification arrives. 'default' means to
play a default sound.
Tweaks are passed transparently through the Home Server so client applications
and push gateways may agree on additional tweaks, for example, how to flash the
notification light on a mobile device.
Push Rules: Conditions:
-----------------------
Override, Underride and Default rules have a list of 'conditions'. All
conditions must hold true for an event in order for a rule to be applied to an
event. A rule with no conditions always matches. Matrix specifies the following
conditions, although if Home Servers wish to support others, they are free to
do so:
event_match
This is a glob pattern match on a field of the event. Parameters:
* 'key': The dot-separated field of the event to match, eg. content.body
* 'pattern': The glob-style pattern to match against. Patterns with no
special glob characters should be treated as having asterisks
prepended and appended when testing the condition.
profile_tag
Matches the profile_tag of the device that the notification would be
delivered to. Parameters:
* 'profile_tag': The profile_tag to match with.
contains_display_name
This matches unencrypted messages where content.body contains the owner's
display name in that room. This is a separate rule because display names may
change and as such it would be hard to maintain a rule that matched the user's
display name. This condition has no parameters.
room_member_count
This matches the current number of members in the room.
* 'is': A decimal integer optionally prefixed by one of, '==', '<', '>',
'>=' or '<='. A prefix of '<' matches rooms where the member count is
strictly less than the given number and so forth. If no prefix is present,
this matches rooms where the member count is exactly equal to the given
number (ie. the same as '==').
Room, Sender, User and Content rules do not have conditions in the same way,
but instead have predefined conditions, the behaviour of which can be configured
using parameters named as described above. In the cases of room and sender
rules, the rule_id of the rule determines its behaviour.
Push Rules: API
---------------
Rules live under a hierarchy in the REST API that resembles::
$PREFIX/pushrules/<scope>/<kind>/<rule_id>
The component parts are as follows:
scope
Either 'global' or 'device/<profile_tag>' to specify global rules or
device rules for the given profile_tag.
kind
The kind of rule, ie. 'override', 'underride', 'sender', 'room', 'content'.
rule_id
The identifier for the rule.
To add or change a rule, a client performs a PUT request to the appropriate URL.
When adding rules of a type that has an ordering, the client can add parameters
that define the priority of the rule:
before
Use 'before' with a rule_id as its value to make the new rule the next-more
important rule with respect to the given rule.
after
This makes the new rule the next-less important rule relative to the given
rule.
All requests to the push rules API also require an access_token as a query
paraemter.
The content of the PUT request is a JSON object with a list of actions under the
'actions' key and either conditions (under the 'conditions' key) or the
appropriate parameters for the rule (under the appropriate key name).
Examples:
To create a rule that suppresses notifications for the room with ID '!dj234r78wl45Gh4D:matrix.org'::
curl -X PUT -H "Content-Type: application/json" -d '{ "actions" : ["dont_notify"] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%21dj234r78wl45Gh4D%3Amatrix.org?access_token=123456"
To suppress notifications for the user '@spambot:matrix.org'::
curl -X PUT -H "Content-Type: application/json" -d '{ "actions" : ["dont_notify"] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/sender/%40spambot%3Amatrix.org?access_token=123456"
To always notify for messages that contain the work 'cake' and set a specific sound (with a rule_id of 'SSByZWFsbHkgbGlrZSBjYWtl')::
curl -X PUT -H "Content-Type: application/json" -d '{ "pattern": "cake", "actions" : ["notify", {"set_sound":"cakealarm.wav"}] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/content/SSByZWFsbHkgbGlrZSBjYWtl?access_token=123456"
To add a rule suppressing notifications for messages starting with 'cake' but ending with 'lie', superseeding the previous rule::
curl -X PUT -H "Content-Type: application/json" -d '{ "pattern": "cake*lie", "actions" : ["notify"] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/content/U3BvbmdlIGNha2UgaXMgYmVzdA?access_token=123456&before=SSByZWFsbHkgbGlrZSBjYWtl"
To add a custom sound for notifications messages containing the word 'beer' in any rooms with 10 members or fewer (with greater importance than the room, sender and content rules)::
curl -X PUT -H "Content-Type: application/json" -d '{ "conditions": [{"kind": "event_match", "key": "content.body", "pattern": "beer" }, {"kind": "room_member_count", "is": "<=10"}], "actions" : ["notify", {"set_sound":"beeroclock.wav"}] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/override/U2VlIHlvdSBpbiBUaGUgRHVrZQ?access_token=123456
To delete rules, a client would just make a DELETE request to the same URL::
curl -X DELETE "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%23spam%3Amatrix.org?access_token=123456"
Retrieving the current ruleset can be done either by fetching individual rules
using the scheme as specified above. This returns the rule in the same format as
would be given in the PUT API with the addition of a rule_id::
curl "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%23spam%3Amatrix.org?access_token=123456"
Returns::
{
"actions": [
"dont_notify"
],
"rule_id": "#spam:matrix.org"
}
Clients can also fetch broader sets of rules by removing path components.
Requesting the root level returns a structure as follows::
{
"device": {
"exampledevice": {
"content": [],
"override": [],
"room": [
{
"actions": [
"dont_notify"
],
"rule_id": "#spam:matrix.org"
}
],
"sender": [],
"underride": []
}
},
"global": {
"content": [],
"override": [],
"room": [],
"sender": [],
"underride": []
}
}
Adding patch components to the request drills down into this structure to filter
to only the requested set of rules.

@ -0,0 +1,32 @@
Push Notifications
==================
Matrix supports push notifications as a first class citizen. Home Servers send
notifications of user events to user-configured HTTP endpoints. User may also
configure a number of rules that determine what events generate notifications.
These are all stored and managed by the users home server such that settings can
be reused between client apps as appropriate.
Nomenclature
------------
Pusher
A 'pusher' is an activity in the Home Server that manages the sending
of HTTP notifications for a single device of a single user.
Push Rules
A push rule is a single rule, configured by a matrix user, that gives
instructions to the Home Server about whether an event should be notified
about and how given a set of conditions. Matrix clients allow the user to
configure these. They create and view them via the Client to Server REST API.
Push Gateway
A push gateway is a server that receives HTTP event notifications from Home
Servers and passes them on to a different protocol such as APNS for iOS
devices or GCM for Android devices. Matrix.org provides a reference push
gateway, 'sygnal'. A client app tells a Home Server what push gateway
to send notifications to when it sets up a pusher.
For information on the client-server API for setting pushers and push rules, see
the Client Server API section. For more information on the format of HTTP
notifications, see the HTTP Notification Protocol section.

@ -0,0 +1,135 @@
Push Notifications: HTTP Notification Protocol
==============================================
This describes the format used by "http" pushers to send notifications of
events.
Notifications are sent as HTTP POST requests to the URL configured when the
pusher is created, but Matrix strongly recommends that the path should be::
/_matrix/push/v1/notify
The body of the POST request is a JSON dictionary. The format
is as follows::
{
"notification": {
"id": "$3957tyerfgewrf384",
"room_id": "!slw48wfj34rtnrf:example.com",
"type": "m.room.message",
"sender": "@exampleuser:matrix.org",
"sender_display_name": "Major Tom",
"room_name": "Mission Control",
"room_alias": "#exampleroom:matrix.org",
"prio": "high",
"content": {
"msgtype": "m.text",
"body": "I'm floating in a most peculiar way."
}
},
"counts": {
"unread" : 2,
"missed_calls": 1
}
"devices": [
{
"app_id": "org.matrix.matrixConsole.ios",
"pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/",
"pushkey_ts": 12345678,
"data" : {
},
"tweaks": {
"sound": "bing.wav"
}
}
]
}
}
The contents of this dictionary are defined as follows:
id
An identifier for this notification that may be used to detect duplicate
notification requests. This is not necessarily the ID of the event that
triggered the notification.
room_id
The ID of the room in which this event occurred.
type
The type of the event as in the event's 'type' field.
sender
The sender of the event as in the corresponding event field.
sender_display_name
The current display name of the sender in the room in which the event
occurred.
room_name
The name of the room in which the event occurred.
room_alias
An alias to display for the room in which the event occurred.
prio
The priority of the notification. Acceptable values are 'high' or 'low. If
omitted, 'high' is assumed. This may be used by push gateways to deliver less
time-sensitive notifications in a way that will preserve battery power on
mobile devices.
content
The 'content' field from the event, if present. If the event had no content
field, this field is omitted.
counts
This is a dictionary of the current number of unacknowledged communications
for the recipient user. Counts whose value is zero are omitted.
unread
The number of unread messages a user has accross all of the rooms they are a
member of.
missed_calls
The number of unacknowledged missed calls a user has accross all rooms of
which they are a member.
device
This is an array of devices that the notification should be sent to.
app_id
The app_id given when the pusher was created.
pushkey
The pushkey given when the pusher was created.
pushkey_ts
The unix timestamp (in seconds) when the pushkey was last updated.
data
A dictionary of additional pusher-specific data. For 'http' pushers, this is
the data dictionary passed in at pusher creation minus the 'url' key.
tweaks
A dictionary of customisations made to the way this notification is to be
presented. These are added by push rules.
sound
Sets the sound file that should be played. 'default' means that a default
sound should be played.
The recipient of an HTTP notification should respond with an HTTP 2xx response
when the notification has been processed. If the endpoint returns an HTTP error
code, the Home Server should retry for a reasonable amount of time with a
reasonable backoff scheme.
The endpoint should return a JSON dictionary as follows::
{
"rejected": [ "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/" ]
}
Whose keys are:
rejected
A list of all pushkeys given in the notification request that are not valid.
These could have been rejected by an upstream gateway because they have
expired or have never been valid. Home Servers must cease sending notification
requests for these pushkeys and remove the associated pushers. It may not
necessarily be the notification in the request that failed: it could be that
a previous notification to the same pushkey failed.
Push: Recommendations for APNS
------------------------------
For sending APNS notifications, the exact format is flexible and up to the
client app and its push gateway to agree on (since APNS requires that the sender
have a private key owned by the app developer, each app must have its own push
gateway). However, Matrix strongly recommends:
* That the APNS token be base64 encoded and used as the pushkey.
* That a different app_id be used for apps on the production and sandbox
APS environments.
* That APNS push gateways do not attempt to wait for errors from the APNS
gateway before returning and instead to store failures and return
'rejected' responses next time that pushkey is used.
Loading…
Cancel
Save