Merge branch 'master' of github.com:matrix-org/matrix-doc
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.
|
@ -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…
Reference in New Issue