diff --git a/api/client-server/v1/application_service.yaml b/api/client-server/v1/application_service.yaml new file mode 100644 index 00000000..1a5ec838 --- /dev/null +++ b/api/client-server/v1/application_service.yaml @@ -0,0 +1,201 @@ +swagger: '2.0' +info: + title: "Matrix Application Service API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: "/" +consumes: + - application/json +produces: + - application/json +paths: + "/transactions/{txnId}": + put: + summary: Send some events to the application service. + description: |- + This API is called by the HS when the HS wants to push an event (or + batch of events) to the AS. + parameters: + - in: path + name: txnId + type: string + description: |- + The transaction ID for this set of events. Homeservers generate + these IDs and they are used to ensure idempotency of requests. + required: true + x-example: "35" + - in: body + name: body + description: A list of events + schema: + type: object + example: |- + { + "events": [ + { + "age": 32, + "content": { + "body": "incoming message", + "msgtype": "m.text" + }, + "event_id": "$14328055551tzaee:localhost", + "origin_server_ts": 1432804485886, + "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", + "type": "m.room.message", + "user_id": "@bob:localhost" + }, + { + "age": 1984, + "content": { + "body": "another incoming message", + "msgtype": "m.text" + }, + "event_id": "$1228055551ffsef:localhost", + "origin_server_ts": 1432804485886, + "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", + "type": "m.room.message", + "user_id": "@bob:localhost" + } + ] + } + description: "Transaction informations" + properties: + events: + type: array + description: A list of events + items: + type: object + title: Event + required: ["events"] + responses: + 200: + description: The transaction was processed successfully. + examples: + application/json: |- + {} + schema: + type: object + + "/rooms/{roomAlias}": + get: + summary: Query if a room alias should exist on the application service. + description: |- + This endpoint is invoked by the homeserver on an application service to query + the existence of a given room alias. The homeserver will only query room + aliases inside the application service's ``aliases`` namespace. The + homeserver will send this request when it receives a request to join a + room alias within the application service's namespace. + parameters: + - in: path + name: roomAlias + type: string + description: The room alias being queried. + required: true + x-example: "#magicforest:example.com" + responses: + 200: + description: |- + The application service indicates that this room alias exists. The + application service MUST have created a room and associated it with + the queried room alias using the client-server API. Additional + information about the room such as its name and topic can be set + before responding. + examples: + application/json: |- + {} + schema: + type: object + 401: + description: |- + The homeserver has not supplied credentials to the application service. + Optional error information can be included in the body of this response. + examples: + application/json: |- + { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" + } + schema: + type: object + 403: + description: |- + The credentials supplied by the homeserver were rejected. + examples: + application/json: |- + { + "errcode": "M_FORBIDDEN" + } + schema: + type: object + 404: + description: |- + The application service indicates that this room alias does not exist. + Optional error information can be included in the body of this response. + examples: + application/json: |- + { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" + } + schema: + type: object + "/users/{userId}": + get: + summary: Query if a user should exist on the application service. + description: |- + This endpoint is invoked by the homeserver on an application service to query + the existence of a given user ID. The homeserver will only query user IDs + inside the application service's ``users`` namespace. The homeserver will + send this request when it receives an event for an unknown user ID in + the application service's namespace. + parameters: + - in: path + name: userId + type: string + description: The user ID being queried. + required: true + x-example: "@alice:example.com" + responses: + 200: + description: |- + The application service indicates that this user exists. The application + service MUST create the user using the client-server API. + examples: + application/json: |- + {} + schema: + type: object + 401: + description: |- + The homeserver has not supplied credentials to the application service. + Optional error information can be included in the body of this response. + examples: + application/json: |- + { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" + } + schema: + type: object + 403: + description: |- + The credentials supplied by the homeserver were rejected. + examples: + application/json: |- + { + "errcode": "M_FORBIDDEN" + } + schema: + type: object + 404: + description: |- + The application service indicates that this user does not exist. + Optional error information can be included in the body of this response. + examples: + application/json: |- + { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" + } + schema: + type: object + diff --git a/specification/application_service_api.rst b/specification/application_service_api.rst index 007be48b..cf2f9d57 100644 --- a/specification/application_service_api.rst +++ b/specification/application_service_api.rst @@ -4,7 +4,7 @@ Application Service API The Matrix client-server API and server-server APIs provide the means to implement a consistent self-contained federated messaging fabric. However, they provide limited means of implementing custom server-side behaviour in Matrix -(e.g. gateways, filters, extensible hooks etc). The Application Service API +(e.g. gateways, filters, extensible hooks etc). The Application Service API (AS API) defines a standard API to allow such extensible functionality to be implemented irrespective of the underlying homeserver implementation. @@ -12,15 +12,18 @@ irrespective of the underlying homeserver implementation. Add in Client-Server services? Overview of bots? Seems weird to be in the spec given it is VERY implementation specific. -Passive Application Services ----------------------------- -"Passive" application services can only observe events from a given home server, -and inject events into a room they are participating in. +Application Services +-------------------- +Application services are passive and can only observe events from a given +homeserver. They can inject events into rooms they are participating in. They cannot prevent events from being sent, nor can they modify the content of the event being sent. In order to observe events from a homeserver, the homeserver needs to be configured to pass certain types of traffic to the application service. This is achieved by manually configuring the homeserver -with information about the AS. +with information about the application service (AS). + +Registration +~~~~~~~~~~~~ .. NOTE:: Previously, application services could register with a homeserver via HTTP @@ -38,7 +41,30 @@ with information about the AS. A better solution would be to somehow mandate that the API done to avoid abuse. -An example HS configuration required to pass traffic to the AS is: +Application services register "namespaces" of user IDs, room aliases and room IDs. +These namespaces are represented as regular expressions. An application service +is said to be "interested" in a given event if one of the IDs in the event match +the regular expression provided by the application service. An application +service can also 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 and look like: + +.. code-block:: yaml + + users: + - exclusive: true + regex: @irc.freenode.net_.* + + +The registration is represented by a series of key-value pairs, which this +specification will present as YAML. An example HS configuration required to pass +traffic to the AS is: .. code-block:: yaml @@ -59,135 +85,15 @@ An example HS configuration required to pass traffic to the AS is: ``as_token`` MUST be unique per application service as this token is used to identify the application service. The homeserver MUST enforce this. -- 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, - e.g: - -.. code-block:: yaml - - users: - - exclusive: true - regex: @irc.freenode.net_.* - Home Server -> Application Service API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This contains application service APIs which are used by the home server. All -application services MUST implement these APIs. -User Query -++++++++++ +Pushing events +++++++++++++++ -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 -++++++++++++++++ -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 -+++++++ -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: +The application service API provides a transaction API for sending a list of +events. Each list of events includes a transaction ID, which works as follows: :: @@ -201,35 +107,45 @@ Data flows: 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). - + events based on the transaction 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. +The events sent to the application service should be linearised, as if they were +from the event stream. The homeserver MUST maintain a queue of transactions to +send to the AS. If the application service cannot be reached, the homeserver +SHOULD backoff exponentially until the application service is reachable again. +As application services cannot *modify* the events in any way, these requests can +be made without blocking other aspects of the homeserver. Homeservers 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. -:: +Querying +++++++++ - PUT /transactions/$transaction_id?access_token=$hs_token - - Request format - { - events: [ - ... - ] - } +The application service API includes two querying APIs: for room aliases and for +user IDs. The application service SHOULD create the queried entity if it desires. +During this process, the application service is blocking the homeserver until the +entity is created and configured. If the homeserver does not receive a response +to this request, the homeserver should retry several times before timing out. This +should result in an HTTP status 408 "Request Timeout" on the client which initiated +this request (e.g. to join a room alias). + +.. admonition:: Rationale + + Blocking the homeserver and expecting the application service to create the entity + using the client-server API is simpler and more flexible than alternative methods + such as returning an initial sync style JSON blob and get the HS to provision + the room/user. This also meant that there didn't need to be a "backchannel" to inform + the application service about information about the entity such as room ID to + room alias mappings. + + +HTTP APIs ++++++++++ + +This contains application service APIs which are used by the home server. All +application services MUST implement these APIs. These APIs are defined below. + +{{application_service_http_api}} .. _create the user: `sect:asapi-permissions`_ @@ -237,7 +153,7 @@ Ordering notes: Client-Server v2 API Extensions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Passive application services can utilise a more powerful version of the +Application services can utilise a more powerful version of the client-server API by identifying itself as an application service to the home server. diff --git a/templating/matrix_templates/units.py b/templating/matrix_templates/units.py index 9a17dd5b..78d1b367 100644 --- a/templating/matrix_templates/units.py +++ b/templating/matrix_templates/units.py @@ -153,7 +153,7 @@ class MatrixUnits(Units): for path in api["paths"]: for method in api["paths"][path]: single_api = api["paths"][path][method] - full_path = api.get("basePath", "") + path + full_path = api.get("basePath", "").rstrip("/") + path endpoint = { "title": single_api.get("summary", ""), "desc": single_api.get("description", single_api.get("summary", "")), @@ -299,7 +299,7 @@ class MatrixUnits(Units): ) ] if len(params_missing_examples) == 0: - path_template = api.get("basePath", "") + path + path_template = api.get("basePath", "").rstrip("/") + path qps = {} body = "" for param in single_api.get("parameters", []): @@ -398,7 +398,7 @@ class MatrixUnits(Units): }) return { - "base": api.get("basePath"), + "base": api.get("basePath").rstrip("/"), "group": group_name, "endpoints": endpoints, }