From e13feb3880423fef63dbd5a2aac262670a43ec2d Mon Sep 17 00:00:00 2001
From: babolivier
Also note that we are currently in the process of migrating module interfaces to this system. While some interfaces might be compatible with it, others still require -configuring modules in another part of Synapse's configuration file. Currently, only the -spam checker interface is compatible with this new system.
-Currently, only the following pre-existing interfaces are compatible with this new system:
+A module is a Python class that uses Synapse's module API to interact with the homeserver. It can register callbacks that Synapse will call on specific operations, as well as web resources to attach to Synapse's web server.
@@ -7530,7 +7535,7 @@ either the output of the module'sparse_config
static method (see b
configuration associated with the module in Synapse's configuration file.
See the documentation for the ModuleApi
class
here.
A module can implement the following static method:
@staticmethod
def parse_config(config: dict) -> dict
@@ -7540,7 +7545,7 @@ module. It may modify it (for example by parsing durations expressed as strings
"5d") into milliseconds, etc.), and return the modified dictionary. It may also verify
that the configuration is correct, and raise an instance of
synapse.module_api.errors.ConfigError
if not.
-Registering a web resource
+Registering a web resource
Modules can register web resources onto Synapse's web server using the following module
API method:
def ModuleApi.register_web_resource(path: str, resource: IResource) -> None
@@ -7558,7 +7563,7 @@ interface (such as Registering a callback
+Registering a callback
Modules can use Synapse's module API to register callbacks. Callbacks are functions that
Synapse will call when performing specific actions. Callbacks must be asynchronous, and
are split in categories. A single module may implement callbacks from multiple categories,
@@ -7567,38 +7572,46 @@ callbacks for.
Modules can register callbacks using one of the module API's register_[...]_callbacks
methods. The callback functions are passed to these methods as keyword arguments, with
the callback name as the argument name and the function as its value. This is demonstrated
-in the example below. A register_[...]_callbacks
method exists for each module type
-documented in this section.
-Spam checker callbacks
+in the example below. A register_[...]_callbacks
method exists for each category.
+Callbacks for each category can be found on their respective page of the
+Synapse documentation website.
+Spam checker callbacks
Spam checker callbacks allow module developers to implement spam mitigation actions for
Synapse instances. Spam checker callbacks can be registered using the module API's
register_spam_checker_callbacks
method.
+Callbacks
The available spam checker callbacks are:
+check_event_for_spam
async def check_event_for_spam(event: "synapse.events.EventBase") -> Union[bool, str]
Called when receiving an event from a client or via federation. The module can return
either a bool
to indicate whether the event must be rejected because of spam, or a str
to indicate the event must be rejected because of spam and to give a rejection reason to
forward to clients.
+user_may_invite
async def user_may_invite(inviter: str, invitee: str, room_id: str) -> bool
Called when processing an invitation. The module must return a bool
indicating whether
the inviter can invite the invitee to the given room. Both inviter and invitee are
represented by their Matrix user ID (e.g. @alice:example.com
).
+user_may_create_room
async def user_may_create_room(user: str) -> bool
Called when processing a room creation request. The module must return a bool
indicating
whether the given user (represented by their Matrix user ID) is allowed to create a room.
+user_may_create_room_alias
async def user_may_create_room_alias(user: str, room_alias: "synapse.types.RoomAlias") -> bool
Called when trying to associate an alias with an existing room. The module must return a
bool
indicating whether the given user (represented by their Matrix user ID) is allowed
to set the given alias.
+user_may_publish_room
async def user_may_publish_room(user: str, room_id: str) -> bool
Called when trying to publish a room to the homeserver's public rooms directory. The
module must return a bool
indicating whether the given user (represented by their
Matrix user ID) is allowed to publish the given room.
+check_username_for_spam
async def check_username_for_spam(user_profile: Dict[str, str]) -> bool
Called when computing search results in the user directory. The module must return a
@@ -7611,6 +7624,7 @@ is represented as a dictionary with the following keys:
The module is given a copy of the original dictionary, so modifying it from within the
module cannot modify a user's profile when included in user directory search results.
+check_registration_for_spam
async def check_registration_for_spam(
email_threepid: Optional[dict],
username: Optional[str],
@@ -7631,6 +7645,7 @@ second item is an IP address. These user agents and IP addresses are the ones th
used during the registration process.
auth_provider_id
: The identifier of the SSO authentication provider, if any.
+check_media_file_for_spam
async def check_media_file_for_spam(
file_wrapper: "synapse.rest.media.v1.media_storage.ReadableFileWrapper",
file_info: "synapse.rest.media.v1._base.FileInfo",
@@ -7638,31 +7653,56 @@ used during the registration process.
Called when storing a local or remote file. The module must return a boolean indicating
whether the given file can be stored in the homeserver's media store.
-Account validity callbacks
-Account validity callbacks allow module developers to add extra steps to verify the
-validity on an account, i.e. see if a user can be granted access to their account on the
-Synapse instance. Account validity callbacks can be registered using the module API's
-register_account_validity_callbacks
method.
-The available account validity callbacks are:
-async def is_user_expired(user: str) -> Optional[bool]
-
-Called when processing any authenticated request (except for logout requests). The module
-can return a bool
to indicate whether the user has expired and should be locked out of
-their account, or None
if the module wasn't able to figure it out. The user is
-represented by their Matrix user ID (e.g. @alice:example.com
).
-If the module returns True
, the current request will be denied with the error code
-ORG_MATRIX_EXPIRED_ACCOUNT
and the HTTP status code 403. Note that this doesn't
-invalidate the user's access token.
-async def on_user_registration(user: str) -> None
+Example
+The example below is a module that implements the spam checker callback
+check_event_for_spam
to deny any message sent by users whose Matrix user IDs are
+mentioned in a configured list, and registers a web resource to the path
+/_synapse/client/list_spam_checker/is_evil
that returns a JSON object indicating
+whether the provided user appears in that list.
+import json
+from typing import Union
+
+from twisted.web.resource import Resource
+from twisted.web.server import Request
+
+from synapse.module_api import ModuleApi
+
+
+class IsUserEvilResource(Resource):
+ def __init__(self, config):
+ super(IsUserEvilResource, self).__init__()
+ self.evil_users = config.get("evil_users") or []
+
+ def render_GET(self, request: Request):
+ user = request.args.get(b"user")[0]
+ request.setHeader(b"Content-Type", b"application/json")
+ return json.dumps({"evil": user in self.evil_users})
+
+
+class ListSpamChecker:
+ def __init__(self, config: dict, api: ModuleApi):
+ self.api = api
+ self.evil_users = config.get("evil_users") or []
+
+ self.api.register_spam_checker_callbacks(
+ check_event_for_spam=self.check_event_for_spam,
+ )
+
+ self.api.register_web_resource(
+ path="/_synapse/client/list_spam_checker/is_evil",
+ resource=IsUserEvilResource(config),
+ )
+
+ async def check_event_for_spam(self, event: "synapse.events.EventBase") -> Union[bool, str]:
+ return event.sender not in self.evil_users
-Called after successfully registering a user, in case the module needs to perform extra
-operations to keep track of them. (e.g. add them to a database table). The user is
-represented by their Matrix user ID.
-Third party rules callbacks
+Third party rules callbacks
Third party rules callbacks allow module developers to add extra checks to verify the
validity of incoming events. Third party event rules callbacks can be registered using
the module API's register_third_party_rules_callbacks
method.
+Callbacks
The available third party rules callbacks are:
+check_event_allowed
async def check_event_allowed(
event: "synapse.events.EventBase",
state_events: "synapse.types.StateMap",
@@ -7690,6 +7730,7 @@ that, it is recommended the module calls event.get_dict()
to get th
dictionary, and modify the returned dictionary accordingly.
Note that replacing the event only works for events sent by local users, not for events
received over federation.
+on_create_room
async def on_create_room(
requester: "synapse.types.Requester",
request_content: dict,
@@ -7703,15 +7744,65 @@ for a list of possible parameters), and a boolean indicating whether the user pe
the request is a server admin.
Modules can modify the request_content
(by e.g. adding events to its initial_state
),
or deny the room's creation by raising a module_api.errors.SynapseError
.
-Presence router callbacks
+check_threepid_can_be_invited
+async def check_threepid_can_be_invited(
+ medium: str,
+ address: str,
+ state_events: "synapse.types.StateMap",
+) -> bool:
+
+Called when processing an invite via a third-party identifier (i.e. email or phone number).
+The module must return a boolean indicating whether the invite can go through.
+check_visibility_can_be_modified
+async def check_visibility_can_be_modified(
+ room_id: str,
+ state_events: "synapse.types.StateMap",
+ new_visibility: str,
+) -> bool:
+
+Called when changing the visibility of a room in the local public room directory. The
+visibility is a string that's either "public" or "private". The module must return a
+boolean indicating whether the change can go through.
+Example
+The example below is a module that implements the third-party rules callback
+check_event_allowed
to censor incoming messages as dictated by a third-party service.
+from typing import Optional, Tuple
+
+from synapse.module_api import ModuleApi
+
+_DEFAULT_CENSOR_ENDPOINT = "https://my-internal-service.local/censor-event"
+
+class EventCensorer:
+ def __init__(self, config: dict, api: ModuleApi):
+ self.api = api
+ self._endpoint = config.get("endpoint", _DEFAULT_CENSOR_ENDPOINT)
+
+ self.api.register_third_party_rules_callbacks(
+ check_event_allowed=self.check_event_allowed,
+ )
+
+ async def check_event_allowed(
+ self,
+ event: "synapse.events.EventBase",
+ state_events: "synapse.types.StateMap",
+ ) -> Tuple[bool, Optional[dict]]:
+ event_dict = event.get_dict()
+ new_event_content = await self.api.http_client.post_json_get_json(
+ uri=self._endpoint, post_json=event_dict,
+ )
+ event_dict["content"] = new_event_content
+ return event_dict
+
+Presence router callbacks
Presence router callbacks allow module developers to specify additional users (local or remote)
to receive certain presence updates from local users. Presence router callbacks can be
registered using the module API's register_presence_router_callbacks
method.
+Callbacks
The available presence router callbacks are:
+get_users_for_states
async def get_users_for_states(
- self,
state_updates: Iterable["synapse.api.UserPresenceState"],
-) -> Dict[str, Set["synapse.api.UserPresenceState"]]:
+) -> Dict[str, Set["synapse.api.UserPresenceState"]]
Requires get_interested_users
to also be registered
Called when processing updates to the presence state of one or more users. This callback can
@@ -7719,9 +7810,9 @@ be used to instruct the server to forward that presence state to specific users.
must return a dictionary that maps from Matrix user IDs (which can be local or remote) to the
UserPresenceState
changes that they should be forwarded.
Synapse will then attempt to send the specified presence updates to each user when possible.
+get_interested_users
async def get_interested_users(
- self,
- user_id: str
+ user_id: str
) -> Union[Set[str], "synapse.module_api.PRESENCE_ALL_USERS"]
Requires get_users_for_states
to also be registered
@@ -7733,383 +7824,84 @@ should return the Matrix user IDs of the users whose presence state they are all
query. The returned users can be local or remote.
Alternatively the callback can return synapse.module_api.PRESENCE_ALL_USERS
to indicate that the user should receive updates from all known users.
-For example, if the user @alice:example.org
is passed to this method, and the Set
-{"@bob:example.com", "@charlie:somewhere.org"}
is returned, this signifies that Alice
-should receive presence updates sent by Bob and Charlie, regardless of whether these users
-share a room.
-Porting an existing module that uses the old interface
-In order to port a module that uses Synapse's old module interface, its author needs to:
-
-- ensure the module's callbacks are all asynchronous.
-- register their callbacks using one or more of the
register_[...]_callbacks
methods
-from the ModuleApi
class in the module's __init__
method (see this section
-for more info).
-
-Additionally, if the module is packaged with an additional web resource, the module
-should register this resource in its __init__
method using the register_web_resource
-method from the ModuleApi
class (see this section for
-more info).
-The module's author should also update any example in the module's configuration to only
-use the new modules
section in Synapse's configuration file (see this section
-for more info).
-Example
-The example below is a module that implements the spam checker callback
-user_may_create_room
to deny room creation to user @evilguy:example.com
, and registers
-a web resource to the path /_synapse/client/demo/hello
that returns a JSON object.
-import json
-
-from twisted.web.resource import Resource
-from twisted.web.server import Request
+Example
+The example below is a module that implements both presence router callbacks, and ensures
+that @alice:example.org
receives all presence updates from @bob:example.com
and
+@charlie:somewhere.org
, regardless of whether Alice shares a room with any of them.
+from typing import Dict, Iterable, Set, Union
from synapse.module_api import ModuleApi
-class DemoResource(Resource):
- def __init__(self, config):
- super(DemoResource, self).__init__()
- self.config = config
-
- def render_GET(self, request: Request):
- name = request.args.get(b"name")[0]
- request.setHeader(b"Content-Type", b"application/json")
- return json.dumps({"hello": name})
-
-
-class DemoModule:
+class CustomPresenceRouter:
def __init__(self, config: dict, api: ModuleApi):
- self.config = config
self.api = api
- self.api.register_web_resource(
- path="/_synapse/client/demo/hello",
- resource=DemoResource(self.config),
- )
-
- self.api.register_spam_checker_callbacks(
- user_may_create_room=self.user_may_create_room,
+ self.api.register_presence_router_callbacks(
+ get_users_for_states=self.get_users_for_states,
+ get_interested_users=self.get_interested_users,
)
- @staticmethod
- def parse_config(config):
- return config
-
- async def user_may_create_room(self, user: str) -> bool:
- if user == "@evilguy:example.com":
- return False
-
- return True
-
-
-This page of the Synapse documentation is now deprecated. For up to date
-documentation on setting up or writing a spam checker module, please see
-this page.
-
-Handling spam in Synapse
-Synapse has support to customize spam checking behavior. It can plug into a
-variety of events and affect how they are presented to users on your homeserver.
-The spam checking behavior is implemented as a Python class, which must be
-able to be imported by the running Synapse.
-Python spam checker class
-The Python class is instantiated with two objects:
-
-- Any configuration (see below).
-- An instance of
synapse.module_api.ModuleApi
.
-
-It then implements methods which return a boolean to alter behavior in Synapse.
-All the methods must be defined.
-There's a generic method for checking every event (check_event_for_spam
), as
-well as some specific methods:
-
-user_may_invite
-user_may_create_room
-user_may_create_room_alias
-user_may_publish_room
-check_username_for_spam
-check_registration_for_spam
-check_media_file_for_spam
-
-The details of each of these methods (as well as their inputs and outputs)
-are documented in the synapse.events.spamcheck.SpamChecker
class.
-The ModuleApi
class provides a way for the custom spam checker class to
-call back into the homeserver internals.
-Additionally, a parse_config
method is mandatory and receives the plugin config
-dictionary. After parsing, It must return an object which will be
-passed to __init__
later.
-Example
-from synapse.spam_checker_api import RegistrationBehaviour
-
-class ExampleSpamChecker:
- def __init__(self, config, api):
- self.config = config
- self.api = api
-
- @staticmethod
- def parse_config(config):
- return config
-
- async def check_event_for_spam(self, foo):
- return False # allow all events
-
- async def user_may_invite(self, inviter_userid, invitee_userid, room_id):
- return True # allow all invites
-
- async def user_may_create_room(self, userid):
- return True # allow all room creations
-
- async def user_may_create_room_alias(self, userid, room_alias):
- return True # allow all room aliases
-
- async def user_may_publish_room(self, userid, room_id):
- return True # allow publishing of all rooms
-
- async def check_username_for_spam(self, user_profile):
- return False # allow all usernames
-
- async def check_registration_for_spam(
- self,
- email_threepid,
- username,
- request_info,
- auth_provider_id,
- ):
- return RegistrationBehaviour.ALLOW # allow all registrations
-
- async def check_media_file_for_spam(self, file_wrapper, file_info):
- return False # allow all media
-
-Configuration
-Modify the spam_checker
section of your homeserver.yaml
in the following
-manner:
-Create a list entry with the keys module
and config
.
-
--
-
module
should point to the fully qualified Python class that implements your
-custom logic, e.g. my_module.ExampleSpamChecker
.
-
--
-
config
is a dictionary that gets passed to the spam checker class.
-
-
-Example
-This section might look like:
-spam_checker:
- - module: my_module.ExampleSpamChecker
- config:
- # Enable or disable a specific option in ExampleSpamChecker.
- my_custom_option: true
-
-More spam checkers can be added in tandem by appending more items to the list. An
-action is blocked when at least one of the configured spam checkers flags it.
-Examples
-The Mjolnir project is a full fledged
-example using the Synapse spam checking API, including a bot for dynamic
-configuration.
-
-This page of the Synapse documentation is now deprecated. For up to date
-documentation on setting up or writing a presence router module, please see
-this page.
-
-Presence Router Module
-Synapse supports configuring a module that can specify additional users
-(local or remote) to should receive certain presence updates from local
-users.
-Note that routing presence via Application Service transactions is not
-currently supported.
-The presence routing module is implemented as a Python class, which will
-be imported by the running Synapse.
-Python Presence Router Class
-The Python class is instantiated with two objects:
-
-- A configuration object of some type (see below).
-- An instance of
synapse.module_api.ModuleApi
.
-
-It then implements methods related to presence routing.
-Note that one method of ModuleApi
that may be useful is:
-async def ModuleApi.send_local_online_presence_to(users: Iterable[str]) -> None
-
-which can be given a list of local or remote MXIDs to broadcast known, online user
-presence to (for those users that the receiving user is considered interested in).
-It does not include state for users who are currently offline, and it can only be
-called on workers that support sending federation. Additionally, this method must
-only be called from the process that has been configured to write to the
-the presence stream.
-By default, this is the main process, but another worker can be configured to do
-so.
-Module structure
-Below is a list of possible methods that can be implemented, and whether they are
-required.
-parse_config
-def parse_config(config_dict: dict) -> Any
-
-Required. A static method that is passed a dictionary of config options, and
-should return a validated config object. This method is described further in
-Configuration.
-get_users_for_states
-async def get_users_for_states(
- self,
- state_updates: Iterable[UserPresenceState],
-) -> Dict[str, Set[UserPresenceState]]:
-
-Required. An asynchronous method that is passed an iterable of user presence
-state. This method can determine whether a given presence update should be sent to certain
-users. It does this by returning a dictionary with keys representing local or remote
-Matrix User IDs, and values being a python set
-of synapse.handlers.presence.UserPresenceState
instances.
-Synapse will then attempt to send the specified presence updates to each user when
-possible.
-get_interested_users
-async def get_interested_users(self, user_id: str) -> Union[Set[str], str]
-
-Required. An asynchronous method that is passed a single Matrix User ID. This
-method is expected to return the users that the passed in user may be interested in the
-presence of. Returned users may be local or remote. The presence routed as a result of
-what this method returns is sent in addition to the updates already sent between users
-that share a room together. Presence updates are deduplicated.
-This method should return a python set of Matrix User IDs, or the object
-synapse.events.presence_router.PresenceRouter.ALL_USERS
to indicate that the passed
-user should receive presence information for all known users.
-For clarity, if the user @alice:example.org
is passed to this method, and the Set
-{"@bob:example.com", "@charlie:somewhere.org"}
is returned, this signifies that Alice
-should receive presence updates sent by Bob and Charlie, regardless of whether these
-users share a room.
-Example
-Below is an example implementation of a presence router class.
-from typing import Dict, Iterable, Set, Union
-from synapse.events.presence_router import PresenceRouter
-from synapse.handlers.presence import UserPresenceState
-from synapse.module_api import ModuleApi
-
-class PresenceRouterConfig:
- def __init__(self):
- # Config options with their defaults
- # A list of users to always send all user presence updates to
- self.always_send_to_users = [] # type: List[str]
-
- # A list of users to ignore presence updates for. Does not affect
- # shared-room presence relationships
- self.blacklisted_users = [] # type: List[str]
-
-class ExamplePresenceRouter:
- """An example implementation of synapse.presence_router.PresenceRouter.
- Supports routing all presence to a configured set of users, or a subset
- of presence from certain users to members of certain rooms.
-
- Args:
- config: A configuration object.
- module_api: An instance of Synapse's ModuleApi.
- """
- def __init__(self, config: PresenceRouterConfig, module_api: ModuleApi):
- self._config = config
- self._module_api = module_api
-
- @staticmethod
- def parse_config(config_dict: dict) -> PresenceRouterConfig:
- """Parse a configuration dictionary from the homeserver config, do
- some validation and return a typed PresenceRouterConfig.
-
- Args:
- config_dict: The configuration dictionary.
-
- Returns:
- A validated config object.
- """
- # Initialise a typed config object
- config = PresenceRouterConfig()
- always_send_to_users = config_dict.get("always_send_to_users")
- blacklisted_users = config_dict.get("blacklisted_users")
-
- # Do some validation of config options... otherwise raise a
- # synapse.config.ConfigError.
- config.always_send_to_users = always_send_to_users
- config.blacklisted_users = blacklisted_users
-
- return config
-
async def get_users_for_states(
self,
- state_updates: Iterable[UserPresenceState],
- ) -> Dict[str, Set[UserPresenceState]]:
- """Given an iterable of user presence updates, determine where each one
- needs to go. Returned results will not affect presence updates that are
- sent between users who share a room.
-
- Args:
- state_updates: An iterable of user presence state updates.
-
- Returns:
- A dictionary of user_id -> set of UserPresenceState that the user should
- receive.
- """
- destination_users = {} # type: Dict[str, Set[UserPresenceState]
-
- # Ignore any updates for blacklisted users
- desired_updates = set()
+ state_updates: Iterable["synapse.api.UserPresenceState"],
+ ) -> Dict[str, Set["synapse.api.UserPresenceState"]]:
+ res = {}
for update in state_updates:
- if update.state_key not in self._config.blacklisted_users:
- desired_updates.add(update)
+ if (
+ update.user_id == "@bob:example.com"
+ or update.user_id == "@charlie:somewhere.org"
+ ):
+ res.setdefault("@alice:example.com", set()).add(update)
- # Send all presence updates to specific users
- for user_id in self._config.always_send_to_users:
- destination_users[user_id] = desired_updates
-
- return destination_users
+ return res
async def get_interested_users(
self,
user_id: str,
- ) -> Union[Set[str], PresenceRouter.ALL_USERS]:
- """
- Retrieve a list of users that `user_id` is interested in receiving the
- presence of. This will be in addition to those they share a room with.
- Optionally, the object PresenceRouter.ALL_USERS can be returned to indicate
- that this user should receive all incoming local and remote presence updates.
-
- Note that this method will only be called for local users.
-
- Args:
- user_id: A user requesting presence updates.
-
- Returns:
- A set of user IDs to return additional presence updates for, or
- PresenceRouter.ALL_USERS to return presence updates for all other users.
- """
- if user_id in self._config.always_send_to_users:
- return PresenceRouter.ALL_USERS
+ ) -> Union[Set[str], "synapse.module_api.PRESENCE_ALL_USERS"]:
+ if user_id == "@alice:example.com":
+ return {"@bob:example.com", "@charlie:somewhere.org"}
return set()
-A note on get_users_for_states
and get_interested_users
-Both of these methods are effectively two different sides of the same coin. The logic
-regarding which users should receive updates for other users should be the same
-between them.
-get_users_for_states
is called when presence updates come in from either federation
-or local users, and is used to either direct local presence to remote users, or to
-wake up the sync streams of local users to collect remote presence.
-In contrast, get_interested_users
is used to determine the users that presence should
-be fetched for when a local user is syncing. This presence is then retrieved, before
-being fed through get_users_for_states
once again, with only the syncing user's
-routing information pulled from the resulting dictionary.
-Their routing logic should thus line up, else you may run into unintended behaviour.
-Configuration
-Once you've crafted your module and installed it into the same Python environment as
-Synapse, amend your homeserver config file with the following.
-presence:
- enabled: true
-
- presence_router:
- module: my_module.ExamplePresenceRouter
- config:
- # Any configuration options for your module. The below is an example.
- # of setting options for ExamplePresenceRouter.
- always_send_to_users: ["@presence_gobbler:example.org"]
- blacklisted_users:
- - "@alice:example.com"
- - "@bob:example.com"
- ...
-
-The contents of config
will be passed as a Python dictionary to the static
-parse_config
method of your class. The object returned by this method will
-then be passed to the __init__
method of your module as config
.
+Account validity callbacks
+Account validity callbacks allow module developers to add extra steps to verify the
+validity on an account, i.e. see if a user can be granted access to their account on the
+Synapse instance. Account validity callbacks can be registered using the module API's
+register_account_validity_callbacks
method.
+The available account validity callbacks are:
+is_user_expired
+async def is_user_expired(user: str) -> Optional[bool]
+
+Called when processing any authenticated request (except for logout requests). The module
+can return a bool
to indicate whether the user has expired and should be locked out of
+their account, or None
if the module wasn't able to figure it out. The user is
+represented by their Matrix user ID (e.g. @alice:example.com
).
+If the module returns True
, the current request will be denied with the error code
+ORG_MATRIX_EXPIRED_ACCOUNT
and the HTTP status code 403. Note that this doesn't
+invalidate the user's access token.
+on_user_registration
+async def on_user_registration(user: str) -> None
+
+Called after successfully registering a user, in case the module needs to perform extra
+operations to keep track of them. (e.g. add them to a database table). The user is
+represented by their Matrix user ID.
+Porting an existing module that uses the old interface
+In order to port a module that uses Synapse's old module interface, its author needs to:
+
+- ensure the module's callbacks are all asynchronous.
+- register their callbacks using one or more of the
register_[...]_callbacks
methods
+from the ModuleApi
class in the module's __init__
method (see this section
+for more info).
+
+Additionally, if the module is packaged with an additional web resource, the module
+should register this resource in its __init__
method using the register_web_resource
+method from the ModuleApi
class (see this section for
+more info).
+The module's author should also update any example in the module's configuration to only
+use the new modules
section in Synapse's configuration file (see this section
+for more info).
Scaling synapse via workers
For small instances it recommended to run Synapse in the default monolith mode.
For larger instances where performance is a concern it can be helpful to split
@@ -12731,7 +12523,7 @@ connection errors.
received for each stream so that on reconneciton it can start streaming
from the correct place. Note: not all RDATA have valid tokens due to
batching. See RdataCommand
for more details.
-Example
+Example
An example iteraction is shown below. Each line is prefixed with '>'
or '<' to indicate which side is sending, these are not included on
the wire:
@@ -13027,7 +12819,7 @@ graph), and one where we remove redundant links (the transitive reduction of the
links graph) e.g. if we have chains C3 -> C2 -> C1
then the link C3 -> C1
would not be stored. Synapse uses the former implementations so that it doesn't
need to recurse to test reachability between chains.
-Example
+Example
An example auth graph would look like the following, where chains have been
formed based on type/state_key and are denoted by colour and are labelled with
(chain ID, sequence number)
. Links are denoted by the arrows (links in grey
--
cgit 1.5.1