Synapse Installation and Configuration Guide
Synapse Installation and Configuration Guide
Installation Instructions
Choosing your server name
Installing Synapse
Installing from source
Platform-specific prerequisites
Debian/Ubuntu/Raspbian
ArchLinux
CentOS/Fedora
macOS
OpenSUSE
OpenBSD
Windows
Prebuilt packages
Docker images and Ansible playbooks
Debian/Ubuntu
[Link] packages
Downstream Debian packages
Downstream Ubuntu packages
Fedora
OpenSUSE
SUSE Linux Enterprise Server
ArchLinux
Void Linux
FreeBSD
OpenBSD
NixOS
Setting up Synapse
Using PostgreSQL
TLS certificates
Client Well-Known URI
Email
Registering a user
Setting up a TURN server
URL previews
Troubleshooting Installation
Choosing your server name
It is important to choose the name for your server before you install Synapse, because it
cannot be changed later.
The server name determines the "domain" part of user-ids for users on your server: these will
all be of the format @user:[Link] . It also determines how other matrix servers will
reach yours for federation.
For a test configuration, set this to the hostname of your server. For a more production-ready
setup, you will probably want to specify your domain ( [Link] ) rather than a matrix-
specific hostname here (in the same way that your email address is probably
user@[Link] rather than user@[Link] ) - but doing so may require more
advanced setup: see Setting up Federation.
Installing Synapse
(Prebuilt packages are available for some platforms - see Prebuilt packages.)
When installing from source please make sure that the Platform-specific prerequisites are
already installed.
System requirements:
mkdir -p ~/synapse
virtualenv -p python3 ~/synapse/env
source ~/synapse/env/bin/activate
pip install --upgrade pip
pip install --upgrade setuptools
pip install matrix-synapse
This will download Synapse from PyPI and install it, along with the python libraries it uses, into
a virtual environment under ~/synapse/env . Feel free to pick a different directory if you prefer.
This Synapse installation can then be later upgraded by using pip again with the update flag:
source ~/synapse/env/bin/activate
pip install -U matrix-synapse
Before you can start Synapse, you will need to generate a configuration file. To do this, run (in
your virtualenv, as before):
cd ~/synapse
python -m [Link] \
--server-name [Link] \
--config-path [Link] \
--generate-config \
--report-stats=[yes|no]
This command will generate you a config file that you can then customise, but it will also
generate a set of keys for you. These keys will allow your homeserver to identify itself to other
homeserver, so don't lose or delete them. It would be wise to back them up somewhere safe.
(If, for whatever reason, you do need to change your homeserver's keys, you may find that
other homeserver have the old key cached. If you update the signing key, you should change
the name of the key in the <server name>.[Link] file (the second word) to something
different. See the spec for more information on key management).
To actually run your new homeserver, pick a working directory for Synapse to run (e.g.
~/synapse ), and:
cd ~/synapse
source env/bin/activate
synctl start
Platform-specific prerequisites
Synapse is written in Python but some of the libraries it uses are written in C. So before we can
install Synapse itself we need a working C compiler and the header files for Python C
extensions.
Debian/Ubuntu/Raspbian
CentOS/Fedora
macOS
xcode-select --install
sudo easy_install pip
sudo pip install virtualenv
brew install pkg-config libffi
On macOS Catalina (10.15) you may need to explicitly install OpenSSL via brew and inform pip
about it so that psycopg2 builds:
OpenSUSE
OpenBSD
A port of Synapse is available under net/synapse . The filesystem underlying the homeserver
directory (defaults to /var/synapse ) has to be mounted with wxallowed (cf. mount(8) ), so
creating a separate filesystem and mounting it to /var/synapse should be taken into
consideration.
To be able to build Synapse's dependency on python the WRKOBJDIR (cf. [Link](5) ) for
building python, too, needs to be on a filesystem mounted with wxallowed (cf. mount(8) ).
Creating a WRKOBJDIR for building python under /usr/local (which on a default OpenBSD
installation is mounted with wxallowed ):
echo WRKOBJDIR_lang/python/3.7=/usr/local/pobj_wxallowed
\\nWRKOBJDIR_lang/python/2.7=/usr/local/pobj_wxallowed >> /etc/[Link]
Building Synapse:
cd /usr/ports/net/synapse
make install
Windows
If you wish to run or develop Synapse on Windows, the Windows Subsystem For Linux provides
a Linux environment on Windows 10 which is capable of using the Debian, Fedora, or source
installation methods. More information about WSL can be found at
[Link] for Windows 10 and
[Link] for Windows Server.
Prebuilt packages
As an alternative to installing from source, prebuilt packages are available for a number of
platforms.
Docker images and Ansible playbooks
Alternatively, Andreas Peters (previously Silvio Fricke) has contributed a Dockerfile to automate
a synapse server in a single Docker image, at [Link]
matrix/tags/
Slavi Pantaleev has created an Ansible playbook, which installs the offical Docker image of
Matrix Synapse along with many other Matrix-related services (Postgres database, Element,
coturn, ma1sd, SSL support, etc.). For more details, see [Link]
docker-ansible-deploy
Debian/Ubuntu
[Link] packages
[Link] provides Debian/Ubuntu packages of the latest stable version of Synapse via
[Link] They are available for Debian 9 (Stretch), Ubuntu 16.04
(Xenial), and later. To use them:
Note: if you followed a previous version of these instructions which recommended using apt-
key add to add an old key from [Link] , you should note that
this key has been revoked. You should remove the old key with sudo apt-key remove
C35EB17E1EAE708E6603A9B3AD0592FE47F0DF61 , and follow the above instructions to update
your configuration.
The fingerprint of the repository signing key (as shown by gpg /usr/share/keyrings/matrix-
[Link] ) is AAF9AE843A7584B5A3E4CD2BCF45A512DE2DA058 .
Downstream Debian packages
We do not recommend using the packages from the default Debian buster repository at this
time, as they are old and suffer from known security vulnerabilities. You can install the latest
version of Synapse from our repository or from buster-backports . Please see the Debian
documentation for information on how to use backports.
If you are using Debian sid or testing, Synapse is available in the default repositories and it
should be possible to install it simply with:
We do not recommend using the packages in the default Ubuntu repository at this time, as
they are old and suffer from known security vulnerabilities. The latest version of Synapse can
be installed from our repository.
Fedora
OpenSUSE
ArchLinux
The quickest way to get up and running with ArchLinux is probably with the community
package [Link] which should
pull in most of the necessary dependencies.
If you encounter an error with lib bcrypt causing an Wrong ELF Class: ELFCLASS32 (x64
Systems), you may need to reinstall py-bcrypt to correctly compile it under the right
architecture. (This should not be needed if installing under virtualenv):
Void Linux
xbps-install -Su
xbps-install -S synapse
FreeBSD
Synapse can be installed via FreeBSD Ports or Packages contributed by Brendan Molloy from:
OpenBSD
As of OpenBSD 6.7 Synapse is available as a pre-compiled binary. The filesystem underlying the
homeserver directory (defaults to /var/synapse ) has to be mounted with wxallowed (cf.
mount(8) ), so creating a separate filesystem and mounting it to /var/synapse should be
taken into consideration.
Installing Synapse:
NixOS
Setting up Synapse
Once you have installed synapse as above, you will need to configure it.
Using PostgreSQL
By default Synapse uses an SQLite database and in doing so trades performance for
convenience. Almost all installations should opt to use PostgreSQL instead. Advantages include:
significant performance improvements due to the superior threading and caching model,
smarter query optimiser
allowing the DB to be run on separate hardware
For information on how to install and use PostgreSQL in Synapse, please see docs/[Link]
SQLite is only acceptable for testing purposes. SQLite should not be used in a production
server. Synapse will perform poorly when using SQLite, especially when participating in large
rooms.
TLS certificates
The default configuration exposes a single HTTP port on the local interface:
[Link] . It is suitable for local testing, but for any practical use, you will need
Synapse's APIs to be served over HTTPS.
The recommended way to do so is to set up a reverse proxy on port 8448 . You can find
documentation on doing so in docs/reverse_proxy.md.
Alternatively, you can configure Synapse to expose an HTTPS port. To do so, you will need to
edit [Link] , as follows:
First, under the listeners section, uncomment the configuration for the TLS-enabled
listener. (Remove the hash sign ( # ) at the start of each line). The relevant lines are like
this:
- port: 8448
type: http
tls: true
resources:
- names: [client, federation]
If you are using your own certificate, be sure to use a .pem file that includes the full
certificate chain including any intermediate certificates (for instance, if using certbot, use
[Link] as your certificate, not [Link] ).
For a more detailed guide to configuring your server for federation, see [Link].
Setting up the client Well-Known URI is optional but if you set it up, it will allow users to enter
their full username (e.g. @user:<server_name> ) into clients which support well-known lookup
to automatically configure the homeserver and identity server URLs. This is useful so that users
don't have to memorize or think about the actual homeserver URL you are using.
{
"[Link]": {
"base_url": "[Link]
}
}
{
"[Link]": {
"base_url": "[Link]
},
"m.identity_server": {
"base_url": "[Link]
}
}
To work in browser based clients, the file must be served with the appropriate Cross-Origin
Resource Sharing (CORS) headers. A recommended value would be Access-Control-Allow-
Origin: * which would allow all browser based clients to view it.
location /.well-known/matrix/client {
return 200 '{"[Link]": {"base_url": "[Link]
default_type application/json;
add_header Access-Control-Allow-Origin *;
}
You should also ensure the public_baseurl option in [Link] is set correctly.
public_baseurl should be set to the URL that clients will use to connect to your server. This is
the same URL you put for the [Link] base_url above.
public_baseurl: "[Link]
It is desirable for Synapse to have the capability to send email. This allows Synapse to send
password reset emails, send verifications when an email address is added to a user's account,
and send email notifications to users when they receive new messages.
To configure an SMTP server for Synapse, modify the configuration section headed email , and
be sure to have at least the smtp_host , smtp_port and notif_from fields filled out. You may
also need to set smtp_user , smtp_pass , and require_transport_security .
If email is not configured, password reset, registration and notifications via email will be
disabled.
Registering a user
The easiest way to create a new user is to do so from a client like Element.
Alternatively, you can do so from the command line. This can be done as follows:
1. If synapse was installed via pip, activate the virtualenv as follows (if Synapse was installed
via a prebuilt package, register_new_matrix_user should already be on the search path):
cd ~/synapse
source env/bin/activate
synctl start # if not already running
2. Run the following command:
This will prompt you to add details for the new user, and will then connect to the running
Synapse to create the new user. For example:
For reliable VoIP calls to be routed via this homeserver, you MUST configure a TURN server. See
docs/[Link] for details.
URL previews
Synapse includes support for previewing URLs, which is disabled by default. To turn it on you
must enable the url_preview_enabled: True config parameter and explicitly specify the IP
ranges that Synapse is not allowed to spider for previewing in the
url_preview_ip_range_blacklist configuration parameter. This is critical from a security
perspective to stop arbitrary Matrix users spidering 'internal' URLs on your network. At the very
least we recommend that your loopback and RFC1918 IP addresses are blacklisted.
This also requires the optional lxml python dependency to be installed. This in turn requires
the libxml2 library to be available - on Debian/Ubuntu this means apt-get install libxml2-
dev , or equivalent for your OS.
Troubleshooting Installation
pip seems to leak lots of memory during installation. For instance, a Linux host with 512MB of
RAM may run out of memory whilst installing Twisted. If this happens, you will have to
individually install the dependencies which are failing, e.g.:
If you are using the [Link] debian/ubuntu packages, the necessary python library will
already be installed, but you will need to ensure the low-level postgres library is installed,
which you can do with apt install libpq5 .
For other pre-built packages, please consult the documentation from the relevant
package.
If you installed synapse in a virtualenv, you can install the library with:
(substituting the path to your virtualenv for ~/synapse/env , if you used a different path).
You will require the postgres development files. These are in the libpq-dev package on
Debian-derived distributions.
Set up database
Assuming your PostgreSQL database user is called postgres , first authenticate as the
database user with:
su - postgres
# Or, if your system uses sudo to get administrative rights
sudo -u postgres bash
The above will create a user called synapse_user , and a database called synapse .
Note that the PostgreSQL database must have the correct encoding set (as shown above),
otherwise it will not be able to store UTF8 strings.
You may need to enable password authentication so synapse_user can connect to the
database. See [Link]
Synapse config
When you are ready to start using PostgreSQL, edit the database section in your config file to
match the following lines:
database:
name: psycopg2
args:
user: <user>
password: <pass>
database: <db>
host: <host>
cp_min: 5
cp_max: 10
All key, values in args are passed to the [Link](..) function, except keys
beginning with cp_ , which are consumed by the twisted adbapi connection pool. See the libpq
documentation for a list of options which can be passed.
You should consider tuning the args.keepalives_* options if there is any danger of the
connection between your homeserver and database dropping, otherwise Synapse may block
for an extended period while it waits for a response from the database server. Example values
might be:
database:
args:
# ... as above
# seconds of inactivity after which TCP should send a keepalive message to the
server
keepalives_idle: 10
# the number of seconds after which a TCP keepalive message that is not
# acknowledged by the server should be retransmitted
keepalives_interval: 10
# the number of TCP keepalives that can be lost before the client's connection
# to the server is considered dead
keepalives_count: 3
Tuning Postgres
The default settings should be fine for most deployments. For larger scale deployments tuning
some of the settings is recommended, details of which can be found at
[Link]
In particular, we've found tuning the following values helpful for performance:
shared_buffers
effective_cache_size
work_mem
maintenance_work_mem
autovacuum_work_mem
Note that the appropriate values for those fields depend on the amount of free memory the
database host has available.
Overview
The script synapse_port_db allows porting an existing synapse server backed by SQLite to
using PostgreSQL. This is done in as a two phase process:
1. Copy the existing SQLite database to a separate location and run the port script against
that offline database.
2. Shut down the server. Rerun the port script to port any data that has come in since taking
the first snapshot. Restart server against the PostgreSQL database.
The port script is designed to be run repeatedly against newer snapshots of the SQLite
database file. This makes it safe to repeat step 1 if there was a delay between taking the
previous snapshot and being ready to do step 2.
It is safe to at any time kill the port script and restart it.
Note that the database may take up significantly more (25% - 100% more) space on disk after
porting to Postgres.
Firstly, shut down the currently running synapse server and copy its database file (typically
[Link] ) to another location. Once the copy is complete, restart synapse. For instance:
./synctl stop
cp [Link] [Link]
./synctl start
cp [Link] [Link]
Edit the database section as described in the section Synapse config above and with the SQLite
snapshot located at [Link] simply run:
If the script took a long time to complete, or time has otherwise passed since the original
snapshot was taken, repeat the previous steps with a newer snapshot.
To complete the conversion shut down the synapse server and run the port script one last
time, e.g. if the SQLite database is at [Link] run:
./synctl stop
mv [Link] [Link]
mv [Link] [Link]
./synctl start
Troubleshooting
If you get an error along the lines of FATAL: Ident authentication failed for user
"synapse_user" , you may need to use an authentication method other than ident :
If the synapse_user user has a password, add the password to the database: section of
[Link] . Then add the following to pg_hba.conf :
If the synapse_user user does not have a password, then a password doesn't have to be
added to [Link] . But the following does need to be added to pg_hba.conf :
Note that line order matters in pg_hba.conf , so make sure that if you do add a new line, it is
inserted before:
Synapse will refuse to set up a new database if it has the wrong values of COLLATE and CTYPE
set, and will log warnings on existing databases. Using different locales can cause issues if the
locale library is updated from underneath the database, or if a different version of the locale is
used on any replicas.
The safest way to fix the issue is to dump the database and recreate it with the correct locale
parameter (as shown above). It is also possible to change the parameters on a live database
and run a REINDEX on the entire database, however extreme care must be taken to avoid
database corruption.
Note that the above may fail with an error about duplicate rows if corruption has already
occurred, and such duplicate rows will need to be manually removed.
Synapse uses Postgres sequences to generate IDs for various tables. A sequence and
associated table can get out of sync if, for example, Synapse has been downgraded and then
upgraded again.
To fix the issue shut down Synapse (including any and all workers) and run the SQL command
included in the error message. Once done Synapse should start successfully.
Using a reverse proxy with Synapse
It is recommended to put a reverse proxy such as nginx, Apache, Caddy, HAProxy or relayd in
front of Synapse. One advantage of doing so is that it means that you can expose the default
https port (443) to Matrix clients without needing to run Synapse with root privileges.
You should remember that Matrix clients and other Matrix servers do not necessarily need to
connect to your server via the same server name or port. Indeed, clients will use port 443 by
default, whereas servers default to port 8448. Where these are different, we refer to the 'client
port' and the 'federation port'. See the Matrix specification for more details of the algorithm
used for federation connections, and [Link] for instructions on setting up delegation.
NOTE: Your reverse proxy must not canonicalise or normalise the requested URI in any way
(for example, by decoding %xx escapes). Beware that Apache will canonicalise URIs unless you
specify nocanon .
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name [Link];
location ~* ^(\/_matrix|\/_synapse\/client) {
proxy_pass [Link]
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
NOTE: Do not add a path after the port in proxy_pass , otherwise nginx will
canonicalise/normalise the URI.
Caddy 1
[Link] {
proxy /_matrix [Link] {
transparent
}
[Link] {
proxy / [Link] {
transparent
}
}
Caddy 2
[Link] {
reverse_proxy /_matrix/* [Link]
reverse_proxy /_synapse/client/* [Link]
}
[Link] {
reverse_proxy [Link]
}
Apache
<VirtualHost *:443>
SSLEngine on
ServerName [Link]
<VirtualHost *:8448>
SSLEngine on
ServerName [Link]
NOTE 2: It appears that Synapse is currently incompatible with the ModSecurity module for
Apache ( mod_security2 ). If you need it enabled for other services on your web server, you can
disable it for Synapse's two VirtualHosts by including the following lines before each of the two
</VirtualHost> above:
<IfModule security2_module>
SecRuleEngine off
</IfModule>
frontend https
bind :::443 v4v6 ssl crt /etc/ssl/haproxy/ strict-sni alpn h2,http/1.1
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
http-request set-header X-Forwarded-For %[src]
frontend matrix-federation
bind :::8448 v4v6 ssl crt /etc/ssl/haproxy/[Link] alpn h2,http/1.1
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
http-request set-header X-Forwarded-For %[src]
default_backend matrix
backend matrix
server matrix [Link]:8008
Relayd
relay "https_traffic" {
listen on egress port 443 tls
protocol "https"
forward to <matrixserver> port 8008 check tcp
forward to <webserver> port 8080 check tcp
}
relay "matrix_federation" {
listen on egress port 8448 tls
protocol "matrix"
forward to <matrixserver> port 8008 check tcp
}
Homeserver Configuration
You will also want to set bind_addresses: ['[Link]'] and x_forwarded: true for port
8008 in [Link] to ensure that client IP addresses are recorded correctly.
Having done so, you can then use [Link] (instead of
[Link] ) as the "Custom server" when connecting to Synapse from
a client.
The synapse Matrix Home Server supports integration with TURN server via the TURN server
REST API. This allows the Home Server to generate credentials that are valid for use on the
TURN server through the use of a secret shared between the Home Server and the TURN
server.
The following sections describe how to install coturn (which implements the TURN REST API)
and integrate it with synapse.
Requirements
For TURN relaying with coturn to work, it must be hosted on a server/endpoint with a public
IP.
Hosting TURN behind a NAT (even with appropriate port forwarding) is known to cause issues
and to often not work.
coturn setup
Initial installation
The TURN daemon coturn is available from a variety of sources such as native package
managers, or installation from source.
Debian installation
1. Download the latest release from github. Unpack it and cd into the directory.
2. Configure it:
./configure
You may need to install libevent2 : if so, you should do so in the way recommended by
your operating system. You can ignore warnings about lack of database support: a
database is unnecessary for this purpose.
make
make install
Configuration
1. Create or edit the config file in /etc/[Link] . The relevant lines, with example
values, are:
use-auth-secret
static-auth-secret=[your secret key here]
realm=[Link]
See [Link] for explanations of the options. One way to generate the static-
auth-secret is with pwgen :
pwgen -s 64 1
A realm must be specified, but its value is somewhat arbitrary. (It is sent to clients as part
of the authentication flow.) It is conventional to set it to be your server name.
2. You will most likely want to configure coturn to write logs somewhere. The easiest way is
normally to send them to the syslog:
syslog
(in which case, the logs will be available via journalctl -u coturn on a systemd system).
Alternatively, coturn can be configured to write to a logfile - check the example config file
supplied with coturn.
3. Consider your security settings. TURN lets users request a relay which will connect to
arbitrary IP addresses and ports. The following configuration is suggested as a minimum
starting point:
# don't let the relay ever try to connect to private IP address ranges within
your network (if any)
# given the turn server is likely behind your firewall, remember to include
any privileged public IPs too.
denied-peer-ip=[Link]-[Link]
denied-peer-ip=[Link]-[Link]
denied-peer-ip=[Link]-[Link]
# consider whether you want to limit the quota of relayed streams per user (or
total) to avoid risk of DoS.
user-quota=12 # 4 streams per video call, so 12 streams = 3 simultaneous
relayed calls per user.
total-quota=1200
In this case, replace the turn: schemes in the turn_uri settings below with turns: .
We recommend that you only try to set up TLS/DTLS once you have set up a basic
installation and got it working.
5. Ensure your firewall allows traffic into the TURN server on the ports you've configured it
to listen on (By default: 3478 and 5349 for TURN traffic (remember to allow both TCP and
UDP traffic), and ports 49152-65535 for the UDP relay.)
6. We do not recommend running a TURN server behind NAT, and are not aware of anyone
doing so successfully.
If you want to try it anyway, you will at least need to tell coturn its external IP address:
external-ip=[Link]
... and your NAT gateway must forward all of the relayed ports directly (eg, port 56789 on
the external IP must be always be forwarded to port 56789 on the internal IP).
If you used the Debian package (or have set up a systemd unit yourself):
bin/turnserver -o
Synapse setup
Your home server configuration file needs the following extra keys:
1. " turn_uris ": This needs to be a yaml list of public-facing URIs for your TURN server to be
given out to your clients. Add separate entries for each transport your TURN server
supports.
2. " turn_shared_secret ": This is the secret shared between your Home server and your
TURN server, so you should set it to the same string you used in [Link].
3. " turn_user_lifetime ": This is the amount of time credentials generated by your Home
Server are valid for (in milliseconds). Shorter times offer less potential for abuse at the
expense of increased traffic between web clients and your home server to refresh
credentials. The TURN REST API specification recommends one day (86400000).
4. " turn_allow_guests ": Whether to allow guest users to use the TURN server. This is
enabled by default, as otherwise VoIP will not work reliably for guests. However, it does
introduce a security risk as it lets guests connect to arbitrary endpoints without having
gone through a CAPTCHA or similar to register a real account.
As an example, here is the relevant section of the config file for [Link] . The turn_uris
are appropriate for TURN servers listening on the default ports, with no TLS.
cd /where/you/run/synapse
./synctl restart
... and then reload any clients (or wait an hour for them to refresh their settings).
Troubleshooting
The normal symptoms of a misconfigured TURN server are that calls between devices on
different networks ring, but get stuck at "call connecting". Unfortunately, troubleshooting this
can be tricky.
Check that your TURN server is not behind NAT. As above, we're not aware of anyone who
has successfully set this up.
Check that you have opened your firewall to allow TCP and UDP traffic to the TURN ports
(normally 3478 and 5479).
Check that you have opened your firewall to allow UDP traffic to the UDP relay ports
(49152-65535 by default).
Some WebRTC implementations (notably, that of Google Chrome) appear to get confused
by TURN servers which are reachable over IPv6 (this appears to be an unexpected side-
effect of its handling of multiple IP addresses as defined by draft-ietf-rtcweb-ip-
handling ).
Try removing any AAAA records for your TURN server, so that it is only reachable over
IPv4.
verbose
... and then see if there are any clues in its logs.
You can test your Matrix homeserver TURN setup with [Link]
Note that this test is not fully reliable yet, so don't be discouraged if the test fails. Here is
the github repo of the source of the tester, where you can file bug reports.
secret=staticAuthSecretHere
Or:
Note: these settings will not take effect unless use-auth-secret and static-auth-
secret are disabled.
Remember to restore the original settings to go back to testing with Matrix clients!
If the TURN server is working correctly, you should see at least one relay entry in the
results.
Delegation
By default, other homeservers will expect to be able to reach yours via your server_name , on
port 8448. For example, if you set your server_name to [Link] (so that your user names
look like @user:[Link] ), other servers will try to connect to yours at
[Link] .
.well-known delegation
To use this method, you need to be able to alter the server_name 's https server to serve the
/.well-known/matrix/server URL. Having an active server (with a valid TLS certificate) serving
your server_name domain is out of the scope of this documentation.
{
"[Link]": "<[Link]>[:<yourport>]"
}
{
"[Link]": "[Link]"
}
With .well-known delegation, federating servers will check for a valid TLS certificate for the
delegated hostname (in our example: [Link] ).
SRV DNS record delegation
It is also possible to do delegation using a SRV DNS record. However, that is considered an
advanced topic since it's a bit complex to set up, and .well-known delegation is already
enough in most cases.
However, if you really need it, you can find some documentation on how such a record should
look like and how Synapse will use it in the Matrix specification.
Delegation FAQ
If your homeserver's APIs are accessible on the default federation port (8448) and the domain
your server_name points to, you do not need any delegation.
For instance, if you registered [Link] and pointed its DNS A record at a fresh server, you
could install Synapse on that host, giving it a server_name of [Link] , and once a reverse
proxy has been set up to proxy all requests sent to the port 8448 and serve TLS certificates for
[Link] , you wouldn't need any delegation set up.
However, if your homeserver's APIs aren't accessible on port 8448 and on the domain
server_name points to, you will need to let other servers know how to find it using delegation.
We no longer actively recommend against using a reverse proxy. Many admins will find it easier
to direct federation traffic to a reverse proxy and manage their own TLS certificates, and this is
a supported configuration.
This is no longer necessary. If you are using a reverse proxy for all of your TLS traffic, then you
can set no_tls: True in the Synapse config.
In that case, the only reason Synapse needs the certificate is to populate a legacy
tls_fingerprints field in the federation API. This is ignored by Synapse 0.99.0 and later, and
the only time pre-0.99 Synapses will check it is when attempting to fetch the server keys - and
generally this is delegated via [Link] , which is running a modern version of Synapse.
Do I need the same certificate for the client and federation port?
No. There is nothing stopping you from using different certificates, particularly if you are using
a reverse proxy.
Upgrading Synapse
Before upgrading check if any special steps are required to upgrade from the version you
currently have installed to the current version of Synapse. The extra instructions that may be
required are listed later in this document.
Check that your versions of Python and PostgreSQL are still supported.
Synapse follows upstream lifecycles for Python _ and PostgreSQL _, and removes support
for versions which are no longer maintained.
.. code:: bash
source ~/synapse/env/bin/activate
2. If Synapse was installed using pip then upgrade to the latest version by running:
.. code:: bash
If Synapse was installed using git then upgrade to the latest version by running:
.. code:: bash
3. Restart Synapse:
.. code:: bash
./synctl restart
To check whether your update was successful, you can check the running server version with:
.. code:: bash
curl [Link]
In general, you will need to undo any changes made during the upgrade process, for example:
pip:
.. code:: bash
source env/bin/activate
Debian:
.. code:: bash
We plan to remove support for the current spam checker interface in August 2021.
More module interfaces will be ported over to this new generic system in future versions of
Synapse.
Upgrading to v1.34.0
If you have set room_invite_state_types to the default value you should simply remove it
from your configuration file. The default value used to be:
.. code:: yaml
If you have customised this value, you should remove room_invite_state_types and
configure room_prejoin_state instead.
Upgrading to v1.33.0
The template can now accept an expiration_ts variable, which represents the unix
timestamp in milliseconds for the future date of which their account has been renewed until.
See the default template <[Link]
v1.33.0/synapse/res/templates/account_renewed.html> _ for an example of usage.
ALso note that a new HTML template, account_previously_renewed.html , has been added.
This is is shown to users when they attempt to renew their account with a valid renewal token
that has already been used before. The default template contents can been found here
<[Link]
v1.33.0/synapse/res/templates/account_previously_renewed.html> _, and can also accept an
expiration_ts variable. This template replaces the error message users would previously see
upon attempting to use a valid renewal token more than once.
Upgrading to v1.32.0
If you have been affected, please downgrade to 1.31.0. You then may need to remove excess
writeahead logs in order for Prometheus to recover. Instructions for doing so are provided
here <[Link] _.
Dropping support for old Python, Postgres and SQLite
versions
In line with our deprecation policy <[Link]
org/synapse/blob/release-v1.32.0/docs/deprecation_policy.md> _, we've dropped support
for Python 3.5 and PostgreSQL 9.5, as they are no longer supported upstream.
This release of Synapse requires Python 3.6+ and PostgresSQL 9.6+ or SQLite 3.22+.
The deprecation of the old endpoint was announced with Synapse 1.28.0 (released on 2021-02-
25).
To avoid the warning, administrators using a reverse proxy should ensure that the reverse
proxy sets X-Forwarded-Proto header to https or http to indicate the protocol used by the
client.
Upgrading to v1.27.0
If your server is configured for single sign-on via an OpenID Connect or OAuth2 identity
provider, you will need to add [synapse public
baseurl]/_synapse/client/oidc/callback to the list of permitted "redirect URIs" at the
identity provider.
If your server is configured for single sign-on via a SAML2 identity provider, you will need
to add [synapse public baseurl]/_synapse/client/saml2/authn_response as a
permitted "ACS location" (also known as "allowed callback URLs") at the identity provider.
The "Issuer" in the "AuthnRequest" to the SAML2 identity provider is also updated to
[synapse public baseurl]/_synapse/client/saml2/[Link] . If your SAML2
identity provider uses this property to validate or otherwise identify Synapse, its
configuration will need to be updated to use the new URL. Alternatively you could create a
new, separate "EntityDescriptor" in your SAML2 identity provider with the new URLs and
leave the URLs in the existing "EntityDescriptor" as they were.
If you have customised the templates names for these templates, it is recommended to verify
they end in .html to ensure autoescape is enabled.
add_threepid.html
add_threepid_failure.html
add_threepid_success.html
notice_expiry.html
notice_expiry.html
notif_mail.html (which, by default, includes [Link] and [Link] )
password_reset.html
password_reset_confirmation.html
password_reset_failure.html
password_reset_success.html
[Link]
registration_failure.html
registration_success.html
sso_account_deactivated.html
sso_auth_bad_user.html
sso_auth_confirm.html
sso_auth_success.html
sso_error.html
sso_login_idp_picker.html
sso_redirect_confirm.html
Upgrading to v1.26.0
.. code:: sql
.. code:: sql
.. code:: sql
.. code:: sql
4. Mark the deltas as not run (so they will re-run on upgrade).
.. code:: sql
Upgrading to v1.25.0
We will attempt to maintain support through March 2021, but without guarantees.
In the future, Synapse will follow upstream schedules for ending support of older versions of
Python and PostgreSQL. Please upgrade to at least Python 3.6 and PostgreSQL 9.6 as soon as
possible.
Blacklisting IP ranges
Synapse v1.25.0 includes new settings, ip_range_blacklist and ip_range_whitelist , for
controlling outgoing requests from Synapse for federation, identity servers, push, and for
checking key validity for third-party invite events. The previous setting,
federation_ip_range_blacklist , is deprecated. The new ip_range_blacklist defaults to
private IP ranges if it is not defined.
If you have customised federation_ip_range_blacklist you should update the setting name
to ip_range_blacklist .
If you have a custom push server that is reached via private IP space you may need to
customise ip_range_blacklist or ip_range_whitelist .
Upgrading to v1.24.0
.. code-block:: python
class MyMappingProvider: def map_user_attributes(self, userinfo, token): # ... your custom logic
... sso_user_id = ... localpart = map_username_to_mxid_localpart(sso_user_id)
/_matrix/client/api/v1/admin
/_matrix/client/unstable/admin
/_matrix/client/r0/admin
/_synapse/admin/v1
The endpoints with /_matrix/client/* prefixes have been removed as of v1.24.0. The Admin
API is now only accessible under:
/_synapse/admin/v1
The only exception is the /admin/whois endpoint, which is also available via the client-
server API <[Link]
admin-whois-userid> _.
The deprecation of the old endpoints was announced with Synapse 1.20.0 (released on 2020-
09-22) and makes it easier for homeserver admins to lock down external access to the Admin
API endpoints.
Upgrading to v1.23.0
The structured and drains logging options are now deprecated and should be replaced by
standard logging configuration of handlers and formatters .
A future will release of Synapse will make using structured: true an error.
Upgrading to v1.22.0
The http_client argument is no longer passed to modules as they are initialised. Instead,
modules are expected to make use of the http_client property on the ModuleApi class.
Modules are now passed a module_api argument during initialisation, which is an instance of
ModuleApi . ModuleApi instances have a http_client property which acts the same as the
http_client argument previously passed to ThirdPartyEventRules modules.
Upgrading to v1.21.0
Note that as of v1.20.0, templates do not need to be included in custom template directories
for Synapse to start. The default templates will be used if a custom template cannot be found.
This page will appear to the user after clicking a password reset link that has been emailed to
them.
To complete password reset, the page must include a way to make a POST request to
/_synapse/client/password_reset/{medium}/submit_token with the query parameters from
the original link, presented as a URL-encoded form. See the file itself for more details.
Updated Single Sign-on HTML Templates
The saml_error.html template was removed from Synapse and replaced with the
sso_error.html template. If your Synapse is configured to use SAML and a custom
sso_redirect_confirm_template_dir configuration then any customisations of the
saml_error.html template will need to be merged into the sso_error.html template. These
templates are similar, but the parameters are slightly different:
Upgrading to v1.18.0
On 10th August, we will remove the latest-py3 tag. Existing per-release tags (such as
v1.18.0-py3 ) will not be removed, but no new -py3 tags will be added.
Upgrading to v1.13.0
If completeness of the room directory is a concern, installations which are affected can be
repaired as follows:
.. code:: sql
2. Restart synapse.
.. code:: sql
3. Downgrade Synapse by following the instructions for your installation method in the
"Rolling back to older versions" section above.
Upgrading to v1.12.0
This version includes a database update which is run as part of the upgrade, and which may
take some time (several hours in the case of a large server). Synapse will not respond to HTTP
requests while this update is taking place.
This is only likely to be a problem in the case of a server which is participating in many rooms.
0. As with all upgrades, it is recommended that you have a recent backup of your database
which can be used for recovery in the event of any problems.
1. As an initial check to see if you will be affected, you can try running the following query
from the psql or sqlite3 console. It is safe to run it while Synapse is still running.
.. code:: sql
SELECT MAX(q.v) FROM ( SELECT ( SELECT [Link] AS v FROM state_events se INNER JOIN
event_json ej USING (event_id) WHERE se.room_id=rooms.room_id AND
[Link]='[Link]' AND se.state_key='' LIMIT 1 ) FROM rooms WHERE
rooms.room_version IS NULL ) q;
This query will take about the same amount of time as the upgrade process: ie, if it takes
5 minutes, then it is likely that Synapse will be unresponsive for 5 minutes during the
upgrade.
If you would prefer to reduce the downtime, continue with the steps below.
2. The easiest workaround for this issue is to manually create a new index before upgrading.
On PostgreSQL, his can be done as follows:
.. code:: sql
The above query may take some time, but is also safe to run while Synapse is running.
We assume that no SQLite users have databases large enough to be affected. If you are
affected, you can run a similar query, omitting the CONCURRENTLY keyword. Note however
that this operation may in itself cause Synapse to stop running for some time. Synapse
admins are reminded that SQLite is not recommended for use outside a test
environment <[Link]
org/synapse/blob/master/[Link]#using-postgresql> _.
3. Once the index has been created, the SELECT query in step 1 above should complete
quickly. It is therefore safe to upgrade to Synapse 1.12.0.
4. Once Synapse 1.12.0 has successfully started and is responding to HTTP requests, the
temporary index can be removed:
.. code:: sql
Upgrading to v1.8.0
Specifying a log_file config option will now cause Synapse to refuse to start, and should be
replaced by with the log_config option. Support for the log_file option was removed in
v1.3.0 and has since had no effect.
Upgrading to v1.7.0
In an attempt to configure Synapse in a privacy preserving way, the default behaviours of
allow_public_rooms_without_auth and allow_public_rooms_over_federation have been
inverted. This means that by default, only authenticated users querying the Client/Server API
will be able to query the room directory, and relatedly that the server will not share room
directory information with other servers over federation.
If your installation does not explicitly set these settings one way or the other and you want
either setting to be true then it will necessary to update your homeserver configuration file
accordingly.
Upgrading to v1.5.0
This release includes a database migration which may take several minutes to complete if there
are a large number (more than a million or so) of entries in the devices table. This is only
likely to a be a problem on very large installations.
Upgrading to v1.4.0
Synapse will expect these files to exist inside the configured template directory, and will fail to
start if they are absent. To view the default templates, see synapse/res/templates
<[Link] _.
It is possible for a user to associate an email address or phone number with their account, for a
number of reasons:
Before an email address or phone number can be added to a user's account, or before such an
address is used to carry out a password-reset, Synapse must confirm the operation with the
owner of the email address or phone number. It does this by sending an email or text giving
the user a link or token to confirm receipt. This process is known as '3pid verification'. ('3pid', or
'threepid', stands for third-party identifier, and we use it to refer to external identifiers such as
email addresses and phone numbers.)
Previous versions of Synapse delegated the task of 3pid verification to an identity server by
default. In most cases this server is [Link] or [Link] .
In Synapse 1.4.0, for security and privacy reasons, the homeserver will no longer delegate this
task to an identity server by default. Instead, the server administrator will need to explicitly
decide how they would like the verification messages to be sent.
In the medium term, the [Link] and [Link] identity servers will disable support for
delegated 3pid verification entirely. However, in order to ease the transition, they will retain the
capability for a limited period. Delegated email verification will be disabled on Monday 2nd
December 2019 (giving roughly 2 months notice). Disabling delegated SMS verification will
follow some time after that once SMS verification support lands in Synapse.
Once delegated 3pid verification support has been disabled in the [Link] and [Link]
identity servers, all Synapse versions that depend on those instances will be unable to verify
email and phone numbers through them. There are no imminent plans to remove delegated
3pid verification from Sydent generally. (Sydent is the identity server project that backs the
[Link] and [Link] instances).
Email
Following upgrade, to continue verifying email (e.g. as part of the
registration process), admins can either:-
Some admins will wish to continue using email verification as part of the
registration process, but will not immediately have an appropriate SMTP server
at hand.
To this end, we will continue to support email verification delegation via the
``[Link]`` and ``[Link]`` identity servers for two months. Support for
delegated email verification will be disabled on Monday 2nd December.
.. code:: yaml
account_threepid_delegates:
email: [Link] # Delegate email sending to [Link]
Phone numbers
Synapse does not support phone-number verification itself, so the only way to maintain the
ability for users to add phone numbers to their accounts will be by continuing to delegate
phone number verification to the [Link] and [Link] identity servers (or another
identity server that supports SMS sending).
.. code:: yaml
The [Link] and [Link] identity servers will continue to support delegated phone
number verification via SMS until such time as it is possible for admins to configure their
servers to perform phone number verification directly. More details will follow in a future
release.
The 'room statistics' engine was heavily reworked in this release (see #5971
<[Link] _), including significant changes to
the database schema, which are not easily reverted. This will cause the room statistics
engine to stop updating when you downgrade.
The room statistics are essentially unused in v1.3.1 (in future versions of Synapse, they
will be used to populate the room directory), so there should be no loss of functionality.
However, the statistics engine will write errors to the logs, which can be avoided by setting
the following in [Link] :
.. code:: yaml
Don't forget to re-enable it when you upgrade again, in preparation for its use in the room
directory!
Upgrading to v1.2.0
Some counter metrics have been renamed, with the old names deprecated. See the metrics
documentation <docs/[Link]#renaming-of-metrics--deprecation-of-old-names-
in-12> _ for details.
Upgrading to v1.1.0
Synapse v1.1.0 removes support for older Python and PostgreSQL versions, as outlined in our
deprecation notice <[Link]
9-4-and-python-2-x> _.
If you use current versions of the [Link]-distributed Debian packages or Docker images,
action is not required.
If you install Synapse in a Python virtual environment, please see "Upgrading to v0.34.0" for
notes on setting up a new virtualenv under Python 3.
Minimum PostgreSQL Version
If using PostgreSQL under Synapse, you will need to use PostgreSQL 9.5 or above. Please see
the PostgreSQL documentation <[Link] _ for
more details on upgrading your database.
Upgrading to v1.0
Note, v1.0 installations will also no longer be able to federate with servers that have not
correctly configured their certificates.
In rare cases, it may be desirable to disable certificate checking: for example, it might be
essential to be able to federate with a given legacy server in a closed federation. This can be
done in one of two ways:-
See the sample configuration file <docs/sample_config.yaml> _ for more details on these
settings.
Email
When a user requests a password reset, Synapse will send an email to the user to confirm the
request.
Previous versions of Synapse delegated the job of sending this email to an identity server. If the
identity server was somehow malicious or became compromised, it would be theoretically
possible to hijack an account through this means.
Therefore, by default, Synapse v1.0 will send the confirmation email itself. If Synapse is not
configured with an SMTP server, password reset via email will be disabled.
To configure an SMTP server for Synapse, modify the configuration section headed email , and
be sure to have at least the smtp_host , smtp_port and notif_from fields filled out. You may
also need to set smtp_user , smtp_pass , and require_transport_security .
If you are absolutely certain that you wish to continue using an identity server for password
resets, set trust_identity_server_for_password_resets to true .
See the sample configuration file <docs/sample_config.yaml> _ for more details on these
settings.
Upgrading to v0.99.0
Please be aware that, before Synapse v1.0 is released around March 2019, you will need to
replace any self-signed certificates with those verified by a root CA. Information on how to do
so can be found at the ACME docs <docs/[Link]> _.
Upgrading to v0.34.0
1. This release is the first to fully support Python 3. Synapse will now run on Python versions
3.5, or 3.6 (as well as 2.7). We recommend switching to Python 3, as it has been shown to
give performance improvements.
For users who have installed Synapse into a virtualenv, we recommend doing this by
creating a new virtualenv. For example::
virtualenv -p python3 ~/synapse/env3
source ~/synapse/env3/bin/activate
pip install matrix-synapse
You can then start synapse as normal, having activated the new virtualenv::
cd ~/synapse
source env3/bin/activate
synctl start
Users who have installed from distribution packages should see the relevant package
documentation. See below for notes on Debian packages.
When upgrading to Python 3, you must make sure that your log files are configured
as UTF-8, by adding encoding: utf8 to the RotatingFileHandler configuration (if
you have one) in your <server>.[Link] file. For example, if your [Link]
file contains::
We are also making available Debian packages which will run Synapse on Python 3. You
can switch to these packages with apt-get install matrix-synapse-py3 , however,
please read debian/NEWS <[Link]
v0.34.0/debian/NEWS> _ before doing so. The existing matrix-synapse packages will
continue to use Python 2 for the time being.
2. This release removes the [Link] from the default list of trusted identity servers.
Upgrading to v0.33.7
This release removes the example email notification templates from res/templates (they are
now internal to the python package). This should only affect you if you (a) deploy your Synapse
instance from a git checkout or a github snapshot URL, and (b) have email notifications
enabled.
If you have email notifications enabled, you should ensure that email.template_dir is either
configured to point at a directory where you have installed customised templates, or leave it
unset to use the default templates.
Upgrading to v0.27.3
This release expands the anonymous usage stats sent if the opt-in report_stats configuration
is set to true . We now capture RSS memory and cpu use at a very coarse level. This requires
administrators to install the optional psutil python module.
We would appreciate it if you could assist by ensuring this module is available and
report_stats is enabled. This will let us see if performance changes to synapse are having an
impact to the general community.
Upgrading to v0.15.0
If you want to use the new URL previewing API (/_matrix/media/r0/preview_url) then you have
to explicitly enable it in the config and update your dependencies dependencies. See
[Link] for details.
Upgrading to v0.11.0
This release includes the option to send anonymous usage stats to [Link], and requires
that administrators explictly opt in or out by setting the report_stats option to either true
or false .
We would really appreciate it if you could help our project out by reporting anonymized usage
statistics from your homeserver. Only very basic aggregate data (e.g. number of users) will be
reported, but it helps us to track the growth of the Matrix community, and helps us to make
Matrix a success, as well as to convince other networks that they should peer with us.
Upgrading to v0.9.0
Application services have had a breaking API change in this version.
They can no longer register themselves with a home server using the AS HTTP API. This
decision was made because a compromised application service with free reign to register any
regex in effect grants full read/write access to the home server if a regex of .* is used. An
attack where a compromised AS re-registers itself with .* was deemed too big of a security
risk to ignore, and so the ability to register with the HS remotely has been removed.
Upgrading to v0.8.0
Servers which use captchas will need to add their public key to::
static/client/register/register_config.js
[Link] = {
recaptcha_public_key: "YOUR_PUBLIC_KEY"
};
This is required in order to support registration fallback (typically used on mobile devices).
Upgrading to v0.7.0
New dependencies are:
pydenticon
simplejson
syutil
matrix-angular-sdk
Upgrading to v0.6.0
To pull in new dependencies, run::
This update includes a change to the database schema. To upgrade you first need to upgrade
the database by running::
Where <db> is the location of the database, <server_name> is the server name as specified in
the synapse configuration, and <signing_key> is the location of the signing key as specified in
the synapse configuration.
This may take some time to complete. Failures of signatures and content hashes can safely be
ignored.
Upgrading to v0.5.1
Depending on precisely when you installed v0.5.0 you may have ended up with a stale release
of the reference matrix webclient installed as a python module. To uninstall it and ensure you
are depending on the latest module, please run::
Upgrading to v0.5.0
The webclient has been split out into a seperate repository/pacakage in this release. Before you
restart your homeserver you will need to pull in the webclient package by running::
The script "[Link]" should be used to upgrade the database. This will
save all user information, such as logins and profiles, but will otherwise purge the database.
This includes messages, which rooms the home server was a member of and room alias
mappings.
If you would like to keep your history, please take a copy of your database file and ask for help
in #matrix:[Link]. The upgrade process is, unfortunately, non trivial and requires human
intervention to resolve any resulting conflicts during the upgrade process.
Before running the command the homeserver should be first completely shutdown. To run it,
simply specify the location of the database, e.g.:
./scripts/[Link] "[Link]"
Once this has successfully completed it will be safe to restart the homeserver. You may notice
that the homeserver takes a few seconds longer to restart than usual as it reinitializes the
database.
On startup of the new version, users can either rejoin remote rooms using room aliases or by
being reinvited. Alternatively, if any other homeserver sends a message to a room that the
homeserver was previously in the local HS will automatically rejoin the room.
Upgrading to v0.4.0
This release needs an updated syutil version. Run::
You will also need to upgrade your configuration as the signing key format has changed. Run::
Upgrading to v0.3.0
This registration API now closely matches the login API. This introduces a bit more backwards
and forwards between the HS and the client, but this improves the overall flexibility of the API.
You can now GET on /register to retrieve a list of valid registration flows. Upon choosing one,
they are submitted in the same way as login, e.g::
Web Client
The VoIP specification has changed between v0.2.0 and v0.3.0. Users should refresh any
browser tabs to get the latest web client code. Users on v0.2.0 of the web client will not be able
to call those on v0.3.0 and vice versa.
Upgrading to v0.2.0
The home server now requires setting up of SSL config before it can run. To automatically
generate default config use::
$ python synapse/app/[Link] \
--server-name [Link] \
--bind-port 8448 \
--config-path [Link] \
--generate-config
This config can be edited if desired, for example to specify a different SSL certificate to use.
Once done you can run the home server using::
Also note that some config options have been renamed, including:
"host" to "server-name"
"database" to "database-path"
"port" to "bind-port" and "unsecure-port"
Upgrading to v0.0.1
This release completely changes the database schema and so requires upgrading it before
starting the new version of the homeserver.
The script "[Link]" should be used to upgrade the database. This will
save all user information, such as logins and profiles, but will otherwise purge the database.
This includes messages, which rooms the home server was a member of and room alias
mappings.
Before running the command the homeserver should be first completely shutdown. To run it,
simply specify the location of the database, e.g.:
./scripts/[Link] "[Link]"
Once this has successfully completed it will be safe to restart the homeserver. You may notice
that the homeserver takes a few seconds longer to restart than usual as it reinitializes the
database.
On startup of the new version, users can either rejoin remote rooms using room aliases or by
being reinvited. Alternatively, if any other homeserver sends a message to a room that the
homeserver was previously in the local HS will automatically rejoin the room.
MSC1711 Certificates FAQ
Historical Note
This document was originally written to guide server admins through the upgrade path
towards Synapse 1.0. Specifically, MSC1711 required that all servers present valid TLS
certificates on their federation API. Admins were encouraged to achieve compliance from
version 0.99.0 (released in February 2019) ahead of version 1.0 (released June 2019) enforcing
the certificate checks.
Much of what follows is now outdated since most admins will have already upgraded, however
it may be of use to those with old installs returning to the project.
If you are setting up a server from scratch you almost certainly should look at the installation
guide instead.
Introduction
The goal of Synapse 0.99.0 is to act as a stepping stone to Synapse 1.0.0. It supports the r0.1
release of the server to server specification, but is compatible with both the legacy Matrix
federation behaviour (pre-r0.1) as well as post-r0.1 behaviour, in order to allow for a smooth
upgrade across the federation.
The most important thing to know is that Synapse 1.0.0 will require a valid TLS certificate on
federation endpoints. Self signed certificates will not be sufficient.
Synapse 0.99.0 makes it easy to configure TLS certificates and will interoperate with both >=
1.0.0 servers as well as existing servers yet to upgrade.
It is critical that all admins upgrade to 0.99.0 and configure a valid TLS certificate. Admins
will have 1 month to do so, after which 1.0.0 will be released and those servers without a valid
certificate will not longer be able to federate with >= 1.0.0 servers.
Full details on how to carry out this configuration change is given below. A timeline and some
frequently asked questions are also given below.
For more details and context on the release of the r0.1 Server/Server API and imminent Matrix
1.0 release, you can also see our main talk from FOSDEM 2019.
Contents
Timeline
Configuring certificates for compatibility with Synapse 1.0
FAQ
Synapse 0.99.0 has just been released, what do I need to do right now?
How do I upgrade?
What will happen if I do not set up a valid federation certificate immediately?
What will happen if I do nothing at all?
When do I need a SRV record or .well-known URI?
Can I still use an SRV record?
I have created a .well-known URI. Do I still need an SRV record?
It used to work just fine, why are you breaking everything?
Can I manage my own certificates rather than having Synapse renew certificates
itself?
Do you still recommend against using a reverse proxy on the federation port?
Do I still need to give my TLS certificates to Synapse if I am using a reverse proxy?
Do I need the same certificate for the client and federation port?
How do I tell Synapse to reload my keys/certificates after I replace them?
Timeline
5th Feb 2019 - Synapse 0.99.0 is released.
0.99.0:
provides support for ACME to make setting up Let's Encrypt certs easy, as well as .well-
known support.
does not enforce that a valid CA cert is present on the federation API, but rather makes it
easy to set one up.
Admins should upgrade and configure a valid CA cert. Homeservers that require a .well-known
entry (see below), should retain their SRV record and use it alongside their .well-known record.
In this case, your server_name points to the host where your Synapse is running. There is no
need to create a .well-known URI or an SRV record, but you will need to give Synapse a valid,
signed, certificate.
If you are using an SRV record, your matrix domain ( server_name ) may not point to the same
host that your Synapse is running on (the 'target domain'). (If it does, you can follow the
recommendation above; otherwise, read on.)
Let's assume that your server_name is [Link] , and your Synapse is hosted at a target
domain of [Link] . Currently you should have an SRV record which looks like:
Synapse 1.0 will expect your server to present a TLS certificate for your server_name
( [Link] in the above example). You can achieve this by acquiring a certificate for the
server_name yourself (for example, using certbot ), and giving it and the key to Synapse via
tls_certificate_path and tls_private_key_path .
If you have an existing reverse proxy set up with correct TLS certificates for your domain, you
can simply route all traffic through the reverse proxy by updating the SRV record appropriately
(or removing it, if the proxy listens on 8448).
This will allow you to keep Synapse on a separate domain, without having to give it a certificate
for the matrix domain.
1. Keep the SRV record in place - it is needed for backwards compatibility with Synapse 0.34
and earlier.
where the target server name is resolved as usual (i.e. SRV lookup, falling back to talking
to port 8448).
Synapse 0.99.0 has just been released, what do I need to do right now?
Upgrade as soon as you can in preparation for Synapse 1.0.0, and update your TLS certificates
as above.
Nothing initially, but once 1.0.0 is in the wild it will not be possible to federate with 1.0.0
servers.
If the admin takes no action at all, and remains on a Synapse < 0.99.0 then the homeserver will
be unable to federate with those who have implemented .well-known. Then, as above, once the
month upgrade window has expired the homeserver will not be able to federate with any
Synapse >= 1.0.0
If your homeserver listens on the default federation port (8448), and your server_name points
to the host that your homeserver runs on, you do not need an SRV record or .well-
known/matrix/server URI.
For instance, if you registered [Link] and pointed its DNS A record at a fresh Upcloud
VPS or similar, you could install Synapse 0.99 on that host, giving it a server_name of
[Link] , and it would automatically generate a valid TLS certificate for you via Let's
Encrypt and no SRV record or .well-known URI would be needed.
This is the common case, although you can add an SRV record or .well-known/matrix/server
URI for completeness if you wish.
However, if your server does not listen on port 8448, or if your server_name does not point to
the host that your homeserver runs on, you will need to let other servers know how to find it.
In this case, you should see "If you do have an SRV record currently" above.
Can I still use an SRV record?
Firstly, if you didn't need an SRV record before (because your server is listening on port 8448 of
your server_name), you certainly don't need one now: the defaults are still the same.
If you previously had an SRV record, you can keep using it provided you are able to give
Synapse a TLS certificate corresponding to your server name. For example, suppose you had
the following SRV record, which directs matrix traffic for [Link] to
[Link]:
In this case, Synapse must be given a certificate for [Link] - or be configured to acquire
one from Let's Encrypt.
If you are unable to give Synapse a certificate for your server_name, you will also need to use a
.well-known URI instead. However, see also "I have created a .well-known URI. Do I still need an
SRV record?".
As of Synapse 0.99, Synapse will first check for the existence of a .well-known URI and follow
any delegation it suggests. It will only then check for the existence of an SRV record.
That means that the SRV record will often be redundant. However, you should remember that
there may still be older versions of Synapse in the federation which do not understand .well-
known URIs, so if you removed your SRV record you would no longer be able to federate with
them.
It is therefore best to leave the SRV record in place for now. Synapse 0.34 and earlier will follow
the SRV record (and not care about the invalid certificate). Synapse 0.99 and later will follow the
.well-known URI, with the correct certificate chain.
We have always wanted Matrix servers to be as easy to set up as possible, and so back when
we started federation in 2014 we didn't want admins to have to go through the cumbersome
process of buying a valid TLS certificate to run a server. This was before Let's Encrypt came
along and made getting a free and valid TLS certificate straightforward. So instead, we adopted
a system based on Perspectives: an approach where you check a set of "notary servers" (in
practice, homeservers) to vouch for the validity of a certificate rather than having it signed by a
CA. As long as enough different notaries agree on the certificate's validity, then it is trusted.
However, in practice this has never worked properly. Most people only use the default notary
server ([Link]), leading to inadvertent centralisation which we want to eliminate.
Meanwhile, we never implemented the full consensus algorithm to query the servers
participating in a room to determine consensus on whether a given certificate is valid. This is
fiddly to get right (especially in face of sybil attacks), and we found ourselves questioning
whether it was worth the effort to finish the work and commit to maintaining a secure
certificate validation system as opposed to focusing on core Matrix development.
Meanwhile, Let's Encrypt came along in 2016, and put the final nail in the coffin of the
Perspectives project (which was already pretty dead). So, the Spec Core Team decided that a
better approach would be to mandate valid TLS certificates for federation alongside the rest of
the Web. More details can be found in MSC1711.
This results in a breaking change, which is disruptive, but absolutely critical for the security
model. However, the existence of Let's Encrypt as a trivial way to replace the old self-signed
certificates with valid CA-signed ones helps smooth things over massively, especially as
Synapse can now automate Let's Encrypt certificate generation if needed.
Yes, you are welcome to manage your certificates yourself. Synapse will only attempt to obtain
certificates from Let's Encrypt if you configure it to do [Link] only requirement is that there is a
valid TLS cert present for federation end points.
We no longer actively recommend against using a reverse proxy. Many admins will find it easier
to direct federation traffic to a reverse proxy and manage their own TLS certificates, and this is
a supported configuration.
If you are using a reverse proxy for all of your TLS traffic, then you can set no_tls: True . In
that case, the only reason Synapse needs the certificate is to populate a legacy 'tls_fingerprints'
field in the federation API. This is ignored by Synapse 0.99.0 and later, and the only time pre-
0.99 Synapses will check it is when attempting to fetch the server keys - and generally this is
delegated via [Link] , which is on 0.99.0.
However, there is a bug in Synapse 0.99.0 4554 which prevents Synapse from starting if you do
not give it a TLS certificate. To work around this, you can give it any TLS certificate at all. This
will be fixed soon.
Do I need the same certificate for the client and federation port?
No. There is nothing stopping you from using different certificates, particularly if you are using
a reverse proxy. However, Synapse will use the same certificate on any ports where TLS is
configured.
Synapse will reload the keys and certificates when it receives a SIGHUP - for example kill -
HUP $(cat [Link]) . Alternatively, simply restart Synapse, though this will result in
downtime while it restarts.
Setting up federation
Federation is the process by which users on different servers can participate in the same room.
For this to work, those other servers must be able to contact yours to send messages.
For this default configuration to work, you will need to listen for TLS connections on port 8448.
The preferred way to do that is by using a reverse proxy: see reverse_proxy.md for instructions
on how to correctly set one up.
In some cases you might not want to run Synapse on the machine that has the server_name as
its public DNS hostname, or you might want federation traffic to use a different port than 8448.
For example, you might want to have your user names look like @user:[Link] , but you
want to run Synapse on [Link] on port 443. This can be done using delegation,
which allows an admin to control where federation traffic should be sent. See [Link] for
instructions on how to set this up.
Once federation has been configured, you should be able to join a room over federation. A
good place to start is #synapse:[Link] - a room for Synapse admins.
Troubleshooting
You can use the federation tester to check if your homeserver is configured correctly.
Alternatively try the JSON API used by the federation tester. Note that you'll have to modify this
URL to replace DOMAIN with your server_name . Hitting the API directly provides extra detail.
The typical failure mode for federation is that when the server tries to join a room, it is rejected
with "401: Unauthorized". Generally this means that other servers in the room could not access
yours. (Joining a room over federation is a complicated dance which requires connections in
both directions).
Another common problem is that people on other servers can't join rooms that you invite them
to. This can be caused by an incorrectly-configured reverse proxy: see reverse_proxy.md for
instructions on how to correctly configure a reverse proxy.
Known issues
HTTP 308 Permanent Redirect redirects are not followed: Due to missing features in the
HTTP library used by Synapse, 308 redirects are currently not followed by federating servers,
which can cause M_UNKNOWN or 401 Unauthorized errors. This may affect users who are
redirecting apex-to-www (e.g. [Link] -> [Link] ), and especially users of the
Kubernetes Nginx Ingress module, which uses 308 redirect codes by default. For those
Kubernetes users, this Stackoverflow post might be helpful. For other users, switching to a 301
Moved Permanently code may be an option. 308 redirect codes will be supported properly in a
future release of Synapse.
Note that the contents below are not intended to be copied and used as the basis for a real
[Link]. Instead, if you are starting from scratch, please generate a fresh config using
Synapse by following the instructions in Installation.
# This file is maintained as an up-to-date snapshot of the default
# [Link] configuration generated by Synapse.
#
# It is intended to act as a reference for the default configuration,
# helping admins keep track of new options and other changes, and compare
# their configs with the current default. As such, many of the actual
# config values shown are placeholders.
#
# It is *not* intended to be copied and used as the basis for a real
# [Link]. Instead, if you are starting from scratch, please generate
# a fresh config using Synapse by following the instructions in [Link].
# Configuration options that take a time period can be set using a number
# followed by a letter. Letters have the following meanings:
# s = second
# m = minute
# h = hour
# d = day
# w = week
# y = year
# For example, setting redaction_retention_period: 5m would remove redacted
# messages from the database after 5 minutes, rather than 5 months.
################################################################################
## Modules ##
## Server ##
# The absolute URL to the web client which /_matrix/client will redirect
# to if 'webclient' is configured under the 'listeners' configuration.
#
# This option can be also set to the filesystem path to the web client
# which will be served at /_matrix/client/ if 'webclient' is configured
# under the 'listeners' configuration, however this is a security risk:
# [Link]
#
#web_client_location: [Link]
# The public-facing base URL that clients use to access this Homeserver (not
# including _matrix/...). This is the same URL a user might enter into the
# 'Custom Homeserver URL' field on their client. If you use Synapse with a
# reverse proxy, this should be the URL to reach Synapse via the proxy.
# Otherwise, it should be the URL to reach Synapse's client HTTP listener (see
# 'listeners' below).
#
#public_baseurl: [Link]
# Set the soft limit on the number of file descriptors synapse can use
# Zero is used to indicate synapse should set the soft limit to the
# hard limit.
#
#soft_file_limit: 0
# If set to 'true', removes the need for authentication to access the server's
# public rooms directory through the client API, meaning that anyone can
# query the room directory. Defaults to 'false'.
#
#allow_public_rooms_without_auth: true
# If set to 'true', allows any other homeserver to fetch the server's public
# rooms directory via federation. Defaults to 'false'.
#
#allow_public_rooms_over_federation: true
# Set the limit on the returned events in the timeline in the get
# and sync operations. The default value is 100. -1 means no upper limit.
#
# Uncomment the following to increase the limit to 5000.
#
#filter_timeline_limit: 5000
# Room searching
#
# If disabled, new messages will not be indexed for searching and users
# will receive errors when searching for messages. Defaults to enabled.
#
#enable_search: false
# List of ports that Synapse should listen on, their purpose and their
# configuration.
#
# Options for each listener include:
#
# port: the TCP port to bind to
#
# bind_addresses: a list of local addresses to listen on. The default is
# 'all local interfaces'.
#
# type: the type of listener. Normally 'http', but other valid options are:
# 'manhole' (see docs/[Link]),
# 'metrics' (see docs/[Link]),
# 'replication' (see docs/[Link]).
#
# tls: set to true to enable TLS for this listener. Will use the TLS
# key/cert specified in tls_private_key_path / tls_certificate_path.
#
# x_forwarded: Only valid for an 'http' listener. Set to true to use the
# X-Forwarded-For header as the client IP. Useful when Synapse is
# behind a reverse-proxy.
#
# resources: Only valid for an 'http' listener. A list of resources to host
# on this port. Options for each resource are:
#
# names: a list of names of HTTP resources. See below for a list of
# valid resource names.
#
# compress: set to true to enable HTTP compression for this resource.
#
# additional_resources: Only valid for an 'http' listener. A map of
# additional endpoints which should be loaded via dynamic modules.
#
# Valid resource names are:
#
# client: the client-server API (/_matrix/client), and the synapse admin
# API (/_synapse/admin). Also implies 'media' and 'static'.
#
# consent: user consent forms (/_matrix/consent). See
# docs/consent_tracking.md.
#
# federation: the server-server API (/_matrix/federation). Also implies
# 'media', 'keys', 'openid'
#
# keys: the key discovery API (/_matrix/keys).
#
# media: the media API (/_matrix/media).
#
# metrics: the metrics interface. See docs/[Link].
#
# openid: OpenID authentication.
#
# replication: the HTTP replication API (/_synapse/replication). See
# docs/[Link].
#
# static: static resources under synapse/static (/_matrix/static). (Mostly
# useful for 'fallback authentication'.)
#
# webclient: A web client. Requires web_client_location to be set.
#
listeners:
# TLS-enabled listener: for when matrix traffic is sent directly to synapse.
#
# Disabled by default. To enable it, uncomment the following. (Note that you
# will also need to give Synapse a TLS key and certificate: see the TLS section
# below.)
#
#- port: 8448
# type: http
# tls: true
# resources:
# - names: [client, federation]
# Unsecure HTTP listener: for when matrix traffic passes through a reverse proxy
# that unwraps TLS.
#
# If you plan to use a reverse proxy, please see
# [Link]
#
- port: 8008
tls: false
type: http
x_forwarded: true
bind_addresses: ['::1', '[Link]']
resources:
- names: [client, federation]
compress: false
# example additional_resources:
#
#additional_resources:
# "/_matrix/my/custom/endpoint":
# module: my_module.CustomRequestHandler
# config: {}
## Homeserver blocking ##
# Global blocking
#
#hs_disabled: false
#hs_disabled_message: 'Human readable reason for why the HS is blocked'
# If enabled, the metrics for the number of monthly active users will
# be populated, however no one will be limited. If limit_usage_by_mau
# is true, this is implied to be true.
#
#mau_stats_only: false
# Sometimes the server admin will want to ensure certain accounts are
# never blocked by mau checking. These accounts are specified here.
#
#mau_limit_reserved_threepids:
# - medium: 'email'
# address: 'reserved_user@[Link]'
# override the error which is returned when the room is too complex.
#
#complexity_error: "This room is too complex."
# How long to keep redacted events in unredacted form in the database. After
# this period redacted events get replaced with their redacted form in the DB.
#
# Defaults to `7d`. Set to `null` to disable.
#
#redaction_retention_period: 28d
# How long to track users' last seen time and IPs in the database.
#
# Defaults to `28d`. Set to `null` to disable clearing out of old rows.
#
#user_ips_max_age: 14d
# Default retention policy. If set, Synapse will apply it to rooms that lack the
# '[Link]' state event. Currently, the value of 'min_lifetime' doesn't
# matter much because Synapse doesn't take it into account yet.
#
#default_policy:
# min_lifetime: 1d
# max_lifetime: 1y
# Server admins can define the settings of the background jobs purging the
# events which lifetime has expired under the 'purge_jobs' section.
#
# If no configuration is provided, a single job will be set up to delete expired
# events in every room daily.
#
# Each job's configuration defines which range of message lifetimes the job
# takes care of. For example, if 'shortest_max_lifetime' is '2d' and
# 'longest_max_lifetime' is '3d', the job will handle purging expired events in
# rooms whose state defines a 'max_lifetime' that's both higher than 2 days, and
# lower than or equal to 3 days. Both the minimum and the maximum value of a
# range are optional, e.g. a job with no 'shortest_max_lifetime' and a
# 'longest_max_lifetime' of '3d' will handle every room with a retention policy
# which 'max_lifetime' is lower than or equal to three days.
#
# The rationale for this per-job configuration is that some rooms might have a
# retention policy with a low 'max_lifetime', where history needs to be purged
# of outdated messages on a more frequent basis than for the rest of the rooms
# (e.g. every 12h), but not want that purge to be performed by a job that's
# iterating over every room it knows, which could be heavy on the server.
#
# If any purge job is configured, it is strongly recommended to have at least
# a single job with neither 'shortest_max_lifetime' nor 'longest_max_lifetime'
# set, or one job without 'shortest_max_lifetime' and one job without
# 'longest_max_lifetime' set. Otherwise some rooms might be ignored, even if
# 'allowed_lifetime_min' and 'allowed_lifetime_max' are set, because capping a
# room's policy to these values is done after the policies are retrieved from
# Synapse's database (which is done using the range specified in a purge job's
# configuration).
#
#purge_jobs:
# - longest_max_lifetime: 3d
# interval: 12h
# - shortest_max_lifetime: 3d
# interval: 1d
# Inhibits the /requestToken endpoints from returning an error that might leak
# information about whether an e-mail address is in use or not on this
# homeserver.
# Note that for some endpoints the error situation is the e-mail already being
# used, and for others the error is entering the e-mail being unused.
# If this option is enabled, instead of returning an error, these endpoints will
# act as if no error happened and return a fake session ID ('sid') to clients.
#
#request_token_inhibit_3pid_errors: true
## TLS ##
# The minimum TLS version that will be used for outbound federation requests.
#
# Defaults to `1`. Configurable to `1`, `1.1`, `1.2`, or `1.3`. Note
# that setting this value higher than `1.2` will prevent federation to most
# of the public Matrix network: only configure it to `1.3` if you have an
# entirely private federation setup and you can ensure TLS 1.3 support.
#
#federation_client_minimum_tls_version: 1.2
## Federation ##
# Report prometheus metrics on the age of PDUs being sent to and received from
# the following domains. This can be used to give an idea of "delay" on inbound
# and outbound federation, though be aware that any delay can be due to problems
# at either end or with the intermediate network.
#
# By default, no domains are monitored in this way.
#
#federation_metrics_domains:
# - [Link]
# - [Link]
# Uncomment to disable device display name lookup over federation. By default, the
# Federation API allows other homeservers to obtain device display names of any
user
# on this homeserver. Defaults to 'true'.
#
#allow_device_name_lookup_over_federation: false
## Caching ##
caches:
# Controls the global cache factor, which is the default cache factor
# for all caches if a specific factor for that cache is not otherwise
# set.
#
# This can also be set by the "SYNAPSE_CACHE_FACTOR" environment
# variable. Setting by environment variable takes priority over
# setting through the config file.
#
# Defaults to 0.5, which will half the size of all caches.
#
#global_factor: 1.0
## Database ##
# The 'database' setting defines the database that synapse uses to store all of
# its data.
#
# 'name' gives the database engine to use: either 'sqlite3' (for SQLite) or
# 'psycopg2' (for PostgreSQL).
#
# 'args' gives options which are passed through to the database engine,
# except for options starting 'cp_', which are used to configure the Twisted
# connection pool. For a reference to valid arguments, see:
# * for sqlite: [Link]
# * for postgres: [Link]
[Link]#LIBPQ-PARAMKEYWORDS
# * for the connection pool:
[Link]
#
#
# Example SQLite configuration:
#
#database:
# name: sqlite3
# args:
# database: /path/to/[Link]
#
#
# Example Postgres configuration:
#
#database:
# name: psycopg2
# args:
# user: synapse_user
# password: secretpassword
# database: synapse
# host: localhost
# port: 5432
# cp_min: 5
# cp_max: 10
#
# For more information on using Synapse with Postgres, see `docs/[Link]`.
#
database:
name: sqlite3
args:
database: DATADIR/[Link]
## Logging ##
## Ratelimiting ##
## Media Store ##
# Enable the media store service in the Synapse master. Uncomment the
# following if you are using a separate media store worker.
#
#enable_media_repo: false
# List of IP address CIDR ranges that the URL preview spider is denied
# from accessing. There are no defaults: you must explicitly
# specify a list for URL previewing to work. You should specify any
# internal services in your network that you do not want synapse to try
# to connect to, otherwise anyone in any Matrix room could cause your
# synapse to issue arbitrary GET requests to your internal services,
# causing serious security issues.
#
# ([Link] and :: are always blacklisted, whether or not they are explicitly
# listed here, since they correspond to unroutable addresses.)
#
# This must be specified if url_preview_enabled is set. It is recommended that
# you uncomment the following list as a starting point.
#
#url_preview_ip_range_blacklist:
# - '[Link]/8'
# - '[Link]/8'
# - '[Link]/12'
# - '[Link]/16'
# - '[Link]/10'
# - '[Link]/24'
# - '[Link]/16'
# - '[Link]/24'
# - '[Link]/15'
# - '[Link]/24'
# - '[Link]/24'
# - '[Link]/24'
# - '[Link]/4'
# - '::1/128'
# - 'fe80::/10'
# - 'fc00::/7'
# - '2001:db8::/32'
# - 'ff00::/8'
# - 'fec0::/10'
# List of IP address CIDR ranges that the URL preview spider is allowed
# to access even if they are specified in url_preview_ip_range_blacklist.
# This is useful for specifying exceptions to wide-ranging blacklisted
# target IP ranges - e.g. for enabling URL previews for a specific private
# website only visible in your network.
#
#url_preview_ip_range_whitelist:
# - '[Link]'
## Captcha ##
# See docs/CAPTCHA_SETUP.md for full details of configuring this.
## TURN ##
# The shared secret used to compute passwords for the TURN server
#
#turn_shared_secret: "YOUR_SHARED_SECRET"
# The Username and password if the TURN server needs them and
# does not use a token
#
#turn_username: "TURNSERVER_USERNAME"
#turn_password: "TURNSERVER_PASSWORD"
## Registration ##
#
# Registration can be rate-limited using the parameters in the "Ratelimiting"
# section of this file.
# Time that a user's session remains valid for, after they log in.
#
# Note that this is not currently compatible with guest logins.
#
# Note also that this is calculated at login time: changes are not applied
# retrospectively to users who have already logged in.
#
# By default, this is infinite.
#
#session_lifetime: 24h
# The user must provide all of the below types of 3PID when registering.
#
#registrations_require_3pid:
# - email
# - msisdn
# The identity server which we suggest that clients should use when users log
# in on this server.
#
# (By default, no suggestion is made, so it is left up to the client.
# This setting is ignored unless public_baseurl is also set.)
#
#default_identity_server: [Link]
# Handle threepid (email/phone etc) registration and password resets through a set
of
# *trusted* identity servers. Note that this allows the configured identity server
to
# reset passwords for accounts!
#
# Be aware that if `email` is not set, and SMTP options have not been
# configured in the email config block, registration and user password resets via
# email will be globally disabled.
#
# Additionally, if `msisdn` is not set, registration and password resets via
msisdn
# will be disabled regardless, and users will not be able to associate an msisdn
# identifier to their account. This is due to Synapse currently not supporting
# any method of sending SMS messages on its own.
#
# To enable using an identity server for operations regarding a particular third-
party
# identifier type, set the value to the URL of that identity server as shown in
the
# examples below.
#
# Servers handling the these requests must answer the `/requestToken` endpoints
defined
# by the Matrix Identity Service API specification:
# [Link]
#
# If a delegate is specified, the config option public_baseurl must also be filled
out.
#
account_threepid_delegates:
#email: [Link] # Delegate email sending to [Link]
#msisdn: [Link] # Delegate SMS sending to this local process
# Whether users are allowed to change their avatar after it has been
# initially set. Useful when provisioning users based on the contents
# of a third-party directory.
#
# Does not apply to server administrators. Defaults to 'true'
#
#enable_set_avatar_url: false
# Whether users can change the 3PIDs associated with their accounts
# (email address and msisdn).
#
# Defaults to 'true'
#
#enable_3pid_changes: false
# Where auto_join_rooms are specified, setting this flag ensures that the
# the rooms exist by creating them when the first user on the
# homeserver registers.
#
# By default the auto-created rooms are publicly joinable from any federated
# server. Use the autocreate_auto_join_rooms_federated and
# autocreate_auto_join_room_preset settings below to customise this behaviour.
#
# Setting to false means that if the rooms are not manually created,
# users cannot be auto-joined since they do not exist.
#
# Defaults to true. Uncomment the following line to disable automatically
# creating auto-join rooms.
#
#autocreate_auto_join_rooms: false
# The room preset to use when auto-creating one of auto_join_rooms. Only has an
# effect if autocreate_auto_join_rooms is true.
#
# This can be one of "public_chat", "private_chat", or "trusted_private_chat".
# If a value of "private_chat" or "trusted_private_chat" is used then
# auto_join_mxid_localpart must also be configured.
#
# Defaults to "public_chat", meaning that the room is joinable by anyone,
including
# federated servers if autocreate_auto_join_rooms_federated is true (the default).
# Uncomment the following to require an invitation to join these rooms.
#
#autocreate_auto_join_room_preset: private_chat
## Account Validity ##
# The period after which an account is valid after its registration. When
# renewing the account, its validity period will be extended by this amount
# of time. This parameter is required when using the account validity
# feature.
#
#period: 6w
# The amount of time before an account's expiry date at which Synapse will
# send an email to the account's email address with a renewal link. By
# default, no such emails are sent.
#
# If you enable this setting, you will also need to fill out the 'email' and
# 'public_baseurl' configuration sections.
#
#renew_at: 1w
# The subject of the email sent out with the renewal link. '%(app)s' can be
# used as a placeholder for the 'app_name' parameter from the 'email'
# section.
#
# Note that the placeholder must be written '%(app)s', including the
# trailing 's'.
#
# If this is not set, a default value is used.
#
#renew_email_subject: "Renew your %(app)s account"
# Directory in which Synapse will try to find templates for the HTML files to
# serve to the user when trying to renew an account. If not set, default
# templates from within the Synapse package will be used.
#
# The currently available templates are:
#
# * account_renewed.html: Displayed to the user after they have successfully
# renewed their account.
#
# * account_previously_renewed.html: Displayed to the user if they attempt to
# renew their account with a token that is valid, but that has already
# been used. In this case the account is not renewed again.
#
# * invalid_token.html: Displayed to the user when they try to renew an account
# with an unknown or invalid renewal token.
#
# See [Link]
for
# default template contents.
#
# The file name of some of these templates can be configured below for legacy
# reasons.
#
#template_dir: "res/templates"
## Metrics ###
# Enable collection and rendering of performance metrics
#
#enable_metrics: false
## API Configuration ##
# Controls for the state that is shared with users who receive an invite
# to a room
#
room_prejoin_state:
# By default, the following state event types are shared with users who
# receive invites to the room:
#
# - [Link].join_rules
# - [Link].canonical_alias
# - [Link]
# - [Link]
# - [Link]
# - [Link]
#
# Uncomment the following to disable these defaults (so that only the event
# types listed in 'additional_event_types' are shared). Defaults to 'false'.
#
#disable_default_event_types: true
# Additional state event types to share with users when they are invited
# to a room.
#
# By default, this list is empty (so only the default event types are shared).
#
#additional_event_types:
# - [Link]
## Signing Keys ##
# The keys that the server used to sign messages with but won't use
# to sign new messages.
#
old_signing_keys:
# For each key, `key` should be the base64-encoded public key, and
# `expired_ts`should be the time (in milliseconds since the unix epoch) that
# it was last used.
#
# It is possible to build an entry from an old [Link] file using the
# `export_signing_key` script which is provided with synapse.
#
# For example:
#
#"ed25519:id": { key: "base64string", expired_ts: 123456789123 }
# Uncomment the following to disable the warning that is emitted when the
# trusted_key_servers include '[Link]'. See above.
#
#suppress_key_server_warning: true
# The signing keys to use when acting as a trusted key server. If not specified
# defaults to the server signing key.
#
# Can contain multiple keys, one per line.
#
#key_server_signing_keys_path: "key_server_signing_keys.key"
# The following settings can be used to make Synapse use a single sign-on
# provider for authentication, instead of its internal password database.
#
# You will probably also want to set the following options to `false` to
# disable the regular login/registration flows:
# * enable_registration
# * password_config.enabled
#
# You will also want to investigate the settings under the "sso" configuration
# section below.
# By default, the user has to go to our login page first. If you'd like
# to allow IdP-initiated login, set 'allow_unsolicited: true' in a
# '[Link]' section:
#
#service:
# sp:
# allow_unsolicited: true
# The examples below are just used to generate our metadata xml, and you
# may well not need them, depending on your setup. Alternatively you
# may need a whole lot more detail - see the pysaml2 docs!
#ui_info:
# display_name:
# - lang: en
# text: "Display Name is the descriptive name of your service."
# description:
# - lang: en
# text: "Description should be a short paragraph explaining the purpose
of the service."
# information_url:
# - lang: en
# text: "[Link]
# privacy_statement_url:
# - lang: en
# text: "[Link]
# keywords:
# - lang: en
# text: ["Matrix", "Element"]
# logo:
# - lang: en
# text: "[Link]
# width: "200"
# height: "80"
#organization:
# name: Example com
# display_name:
# - ["Example co", "en"]
# url: "[Link]
#contact_person:
# - given_name: Bob
# sur_name: "the Sysadmin"
# email_address": ["admin@[Link]"]
# contact_type": technical
# The lifetime of a SAML session. This defines how long a user has to
# complete the authentication process, if allow_unsolicited is unset.
# The default is 15 minutes.
#
#saml_session_lifetime: 5m
# The mapping system to use for mapping the saml attribute onto a
# matrix ID.
#
# Options include:
# * 'hexencode' (which maps unpermitted characters to '=xx')
# * 'dotreplace' (which replaces unpermitted characters with
# '.').
# The default is 'hexencode'.
#
# Note: This used to be configured by the
# saml2_config.mxid_mapping option. If that is still defined, its
# value will be used instead.
#
#mxid_mapping: dotreplace
# If the metadata XML contains multiple IdP entities then the `idp_entityid`
# option must be set to the entity to redirect users to.
#
# Most deployments only have a single IdP entity and so should omit this
# option.
#
#idp_entityid: '[Link]
# List of OpenID Connect (OIDC) / OAuth 2.0 identity providers, for registration
# and login.
#
# Options for each entry include:
#
# idp_id: a unique identifier for this identity provider. Used internally
# by Synapse; should be a single word such as 'github'.
#
# Note that, if this is changed, users authenticating via that provider
# will no longer be recognised as the same user!
#
# (Use "oidc" here if you are migrating from an old "oidc_config"
# configuration.)
#
# idp_name: A user-facing name for this identity provider, which is used to
# offer the user a choice of login mechanisms.
#
# idp_icon: An optional icon for this identity provider, which is presented
# by clients and Synapse's own IdP picker page. If given, must be an
# MXC URI of the format mxc://<server-name>/<media-id>. (An easy way to
# obtain such an MXC URI is to upload an image to an (unencrypted) room
# and then copy the "url" from the source of the event.)
#
# idp_brand: An optional brand for this identity provider, allowing clients
# to style the login flow according to the identity provider in question.
# See the spec for possible options here.
#
# discover: set to 'false' to disable the use of the OIDC discovery mechanism
# to discover endpoints. Defaults to true.
#
# issuer: Required. The OIDC issuer. Used to validate tokens and (if discovery
# is enabled) to discover the provider's endpoints.
#
# client_id: Required. oauth2 client id to use.
#
# client_secret: oauth2 client secret to use. May be omitted if
# client_secret_jwt_key is given, or if client_auth_method is 'none'.
#
# client_secret_jwt_key: Alternative to client_secret: details of a key used
# to create a JSON Web Token to be used as an OAuth2 client secret. If
# given, must be a dictionary with the following properties:
#
# key: a pem-encoded signing key. Must be a suitable key for the
# algorithm specified. Required unless 'key_file' is given.
#
# key_file: the path to file containing a pem-encoded signing key file.
# Required unless 'key' is given.
#
# jwt_header: a dictionary giving properties to include in the JWT
# header. Must include the key 'alg', giving the algorithm used to
# sign the JWT, such as "ES256", using the JWA identifiers in
# RFC7518.
#
# jwt_payload: an optional dictionary giving properties to include in
# the JWT payload. Normally this should include an 'iss' key.
#
# client_auth_method: auth method to use when exchanging the token. Valid
# values are 'client_secret_basic' (default), 'client_secret_post' and
# 'none'.
#
# scopes: list of scopes to request. This should normally include the "openid"
# scope. Defaults to ["openid"].
#
# authorization_endpoint: the oauth2 authorization endpoint. Required if
# provider discovery is disabled.
#
# token_endpoint: the oauth2 token endpoint. Required if provider discovery is
# disabled.
#
# userinfo_endpoint: the OIDC userinfo endpoint. Required if discovery is
# disabled and the 'openid' scope is not requested.
#
# jwks_uri: URI where to fetch the JWKS. Required if discovery is disabled and
# the 'openid' scope is used.
#
# skip_verification: set to 'true' to skip metadata verification. Use this if
# you are connecting to a provider that is not OpenID Connect compliant.
# Defaults to false. Avoid this in production.
#
# user_profile_method: Whether to fetch the user profile from the userinfo
# endpoint. Valid values are: 'auto' or 'userinfo_endpoint'.
#
# Defaults to 'auto', which fetches the userinfo endpoint if 'openid' is
# included in 'scopes'. Set to 'userinfo_endpoint' to always fetch the
# userinfo endpoint.
#
# allow_existing_users: set to 'true' to allow a user logging in via OIDC to
# match a pre-existing account instead of failing. This could be used if
# switching from password logins to OIDC. Defaults to false.
#
# user_mapping_provider: Configuration for how attributes returned from a OIDC
# provider are mapped onto a matrix user. This setting has the following
# sub-properties:
#
# module: The class name of a custom mapping module. Default is
# '[Link]'.
# See [Link]
org/synapse/blob/master/docs/sso_mapping_providers.md#openid-mapping-providers
# for information on implementing a custom mapping provider.
#
# config: Configuration for the mapping provider module. This section will
# be passed as a Python dictionary to the user mapping provider
# module's `parse_config` method.
#
# For the default provider, the following settings are available:
#
# subject_claim: name of the claim containing a unique identifier
# for the user. Defaults to 'sub', which OpenID Connect
# compliant providers should provide.
#
# localpart_template: Jinja2 template for the localpart of the MXID.
# If this is not set, the user will be prompted to choose their
# own username (see 'sso_auth_account_details.html' in the 'sso'
# section of this file).
#
# display_name_template: Jinja2 template for the display name to set
# on first login. If unset, no displayname will be set.
#
# email_template: Jinja2 template for the email address of the user.
# If unset, no email address will be added to the account.
#
# extra_attributes: a map of Jinja2 templates for extra attributes
# to send back to the client during login.
# Note that these are non-standard and clients will ignore them
# without modifications.
#
# When rendering, the Jinja2 templates are given a 'user' variable,
# which is set to the claims returned by the UserInfo Endpoint and/or
# in the ID Token.
#
# It is possible to configure Synapse to only allow logins if certain attributes
# match particular values in the OIDC userinfo. The requirements can be listed
under
# `attribute_requirements` as shown below. All of the listed attributes must
# match for the login to be permitted. Additional attributes can be added to
# userinfo by expanding the `scopes` section of the OIDC config to retrieve
# additional information from the OIDC provider.
#
# If the OIDC claim is a list, then the attribute must match any value in the
list.
# Otherwise, it must exactly match the value of the claim. Using the example
# below, the `family_name` claim MUST be "Stephensson", but the `groups`
# claim MUST contain "admin".
#
# attribute_requirements:
# - attribute: family_name
# value: "Stephensson"
# - attribute: groups
# value: "admin"
#
# See [Link]
# for information on how to configure these options.
#
# For backwards compatibility, it is also possible to configure a single OIDC
# provider via an 'oidc_config' setting. This is now deprecated and admins are
# advised to migrate to the 'oidc_providers' format. (When doing that migration,
# use 'oidc' for the idp_id to ensure that existing users continue to be
# recognised.)
#
oidc_providers:
# Generic example
#
#- idp_id: my_idp
# idp_name: "My OpenID provider"
# idp_icon: "mxc://[Link]/mediaid"
# discover: false
# issuer: "[Link]
# client_id: "provided-by-your-issuer"
# client_secret: "provided-by-your-issuer"
# client_auth_method: client_secret_post
# scopes: ["openid", "profile"]
# authorization_endpoint: "[Link]
# token_endpoint: "[Link]
# userinfo_endpoint: "[Link]
# jwks_uri: "[Link]
# skip_verification: true
# user_mapping_provider:
# config:
# subject_claim: "id"
# localpart_template: "{{ [Link] }}"
# display_name_template: "{{ [Link] }}"
# email_template: "{{ [Link] }}"
# attribute_requirements:
# - attribute: userGroup
# value: "synapseUsers"
# Enable Central Authentication Service (CAS) for registration and login.
#
cas_config:
# Uncomment the following to enable authorization against a CAS server.
# Defaults to false.
#
#enabled: true
# Directory in which Synapse will try to find the template files below.
# If not set, or the files named below are not found within the template
# directory, default templates from within the Synapse package will be used.
#
# Synapse will look for the following templates in this directory:
#
# * HTML page to prompt the user to choose an Identity Provider during
# login: 'sso_login_idp_picker.html'.
#
# This is only used if multiple SSO Identity Providers are configured.
#
# When rendering, this template is given the following variables:
# * redirect_url: the URL that the user will be redirected to after
# login.
#
# * server_name: the homeserver's name.
#
# * providers: a list of available Identity Providers. Each element is
# an object with the following attributes:
#
# * idp_id: unique identifier for the IdP
# * idp_name: user-facing name for the IdP
# * idp_icon: if specified in the IdP config, an MXC URI for an icon
# for the IdP
# * idp_brand: if specified in the IdP config, a textual identifier
# for the brand of the IdP
#
# The rendered HTML page should contain a form which submits its results
# back as a GET request, with the following query parameters:
#
# * redirectUrl: the client redirect URI (ie, the `redirect_url` passed
# to the template)
#
# * idp: the 'idp_id' of the chosen IDP.
#
# * HTML page to prompt new users to enter a userid and confirm other
# details: 'sso_auth_account_details.html'. This is only shown if the
# SSO implementation (with any user_mapping_provider) does not return
# a localpart.
#
# When rendering, this template is given the following variables:
#
# * server_name: the homeserver's name.
#
# * idp: details of the SSO Identity Provider that the user logged in
# with: an object with the following attributes:
#
# * idp_id: unique identifier for the IdP
# * idp_name: user-facing name for the IdP
# * idp_icon: if specified in the IdP config, an MXC URI for an icon
# for the IdP
# * idp_brand: if specified in the IdP config, a textual identifier
# for the brand of the IdP
#
# * user_attributes: an object containing details about the user that
# we received from the IdP. May have the following attributes:
#
# * display_name: the user's display_name
# * emails: a list of email addresses
#
# The template should render a form which submits the following fields:
#
# * username: the localpart of the user's chosen user id
#
# * HTML page allowing the user to consent to the server's terms and
# conditions. This is only shown for new users, and only if
# `user_consent.require_at_registration` is set.
#
# When rendering, this template is given the following variables:
#
# * server_name: the homeserver's name.
#
# * user_id: the user's matrix proposed ID.
#
# * user_profile.display_name: the user's proposed display name, if any.
#
# * consent_version: the version of the terms that the user will be
# shown
#
# * terms_url: a link to the page showing the terms.
#
# The template should render a form which submits the following fields:
#
# * accepted_version: the version of the terms accepted by the user
# (ie, 'consent_version' from the input variables).
#
# * HTML page for a confirmation step before redirecting back to the client
# with the login token: 'sso_redirect_confirm.html'.
#
# When rendering, this template is given the following variables:
#
# * redirect_url: the URL the user is about to be redirected to.
#
# * display_url: the same as `redirect_url`, but with the query
# parameters stripped. The intention is to have a
# human-readable URL to show to users, not to use it as
# the final address to redirect to.
#
# * server_name: the homeserver's name.
#
# * new_user: a boolean indicating whether this is the user's first time
# logging in.
#
# * user_id: the user's matrix ID.
#
# * user_profile.avatar_url: an MXC URI for the user's avatar, if any.
# None if the user has not set an avatar.
#
# * user_profile.display_name: the user's display name. None if the user
# has not set a display name.
#
# * HTML page which notifies the user that they are authenticating to confirm
# an operation on their account during the user interactive authentication
# process: 'sso_auth_confirm.html'.
#
# When rendering, this template is given the following variables:
# * redirect_url: the URL the user is about to be redirected to.
#
# * description: the operation which the user is being asked to confirm
#
# * idp: details of the Identity Provider that we will use to confirm
# the user's identity: an object with the following attributes:
#
# * idp_id: unique identifier for the IdP
# * idp_name: user-facing name for the IdP
# * idp_icon: if specified in the IdP config, an MXC URI for an icon
# for the IdP
# * idp_brand: if specified in the IdP config, a textual identifier
# for the brand of the IdP
#
# * HTML page shown after a successful user interactive authentication
session:
# 'sso_auth_success.html'.
#
# Note that this page must include the JavaScript which notifies of a
successful authentication
# (see [Link]
#
# This template has no additional variables.
#
# * HTML page shown after a user-interactive authentication session which
# does not map correctly onto the expected user: 'sso_auth_bad_user.html'.
#
# When rendering, this template is given the following variables:
# * server_name: the homeserver's name.
# * user_id_to_verify: the MXID of the user that we are trying to
# validate.
#
# * HTML page shown during single sign-on if a deactivated user (according to
Synapse's database)
# attempts to login: 'sso_account_deactivated.html'.
#
# This template has no additional variables.
#
# * HTML page to display to users if something goes wrong during the
# OpenID Connect authentication process: 'sso_error.html'.
#
# When rendering, this template is given two variables:
# * error: the technical name of the error
# * error_description: a human-readable message for the error
#
# You can see the default templates at:
# [Link]
#
#template_dir: "res/templates"
# JSON web token integration. The following settings can be used to make
# Synapse JSON web tokens for authentication, instead of its internal
# password database.
#
# Each JSON Web Token needs to contain a "sub" (subject) claim, which is
# used as the localpart of the mxid.
#
# Additionally, the expiration time ("exp"), not before time ("nbf"),
# and issued at ("iat") claims are validated if present.
#
# Note that this is a non-standard login type and client support is
# expected to be non-existent.
#
# See [Link]
#
#jwt_config:
# Uncomment the following to enable authorization using JSON web
# tokens. Defaults to false.
#
#enabled: true
# This is either the private shared secret or the public key used to
# decode the contents of the JSON web token.
#
# Required if 'enabled' is true.
#
#secret: "provided-by-your-issuer"
password_config:
# Uncomment to disable password login
#
#enabled: false
ui_auth:
# The amount of time to allow a user-interactive authentication session
# to be active.
#
# This defaults to 0, meaning the user is queried for their credentials
# before every action, but this can be overridden to allow a single
# validation to be re-used. This weakens the protections afforded by
# the user-interactive authentication process, by allowing for multiple
# (and potentially different) operations to use the same validation session.
#
# This is ignored for potentially "dangerous" operations (including
# deactivating an account, modifying an account password, and
# adding a 3PID).
#
# Uncomment below to allow for credential validation to last for 15
# seconds.
#
#session_timeout: "15s"
# The port on the mail server for outgoing SMTP. Defaults to 25.
#
#smtp_port: 587
# app_name defines the default value for '%(app)s' in notif_from and email
# subjects. It defaults to 'Matrix'.
#
#app_name: my_branded_matrix_server
# Uncomment the following to enable sending emails for messages that the user
# has missed. Disabled by default.
#
#enable_notifs: true
# Custom URL for client links within the email notifications. By default
# links will be based on "[Link]
#
# (This setting used to be called riot_base_url; the old name is still
# supported for backwards-compatibility but is now deprecated.)
#
#client_base_url: "[Link]
# Configure the time that a validation email will expire after sending.
# Defaults to 1h.
#
#validation_token_lifetime: 15m
# The web client location to direct users to during an invite. This is passed
# to the identity server as the [Link].web_client_location key. Defaults
# to unset, giving no guidance to the identity server.
#
#invite_client_location: [Link]
# Directory in which Synapse will try to find the template files below.
# If not set, or the files named below are not found within the template
# directory, default templates from within the Synapse package will be used.
#
# Synapse will look for the following templates in this directory:
#
# * The contents of email notifications of missed events: 'notif_mail.html' and
# 'notif_mail.txt'.
#
# * The contents of account expiry notice emails: 'notice_expiry.html' and
# 'notice_expiry.txt'.
#
# * The contents of password reset emails sent by the homeserver:
# 'password_reset.html' and 'password_reset.txt'
#
# * An HTML page that a user will see when they follow the link in the password
# reset email. The user will be asked to confirm the action before their
# password is reset: 'password_reset_confirmation.html'
#
# * HTML pages for success and failure that a user will see when they confirm
# the password reset flow using the page above: 'password_reset_success.html'
# and 'password_reset_failure.html'
#
# * The contents of address verification emails sent during registration:
# '[Link]' and '[Link]'
#
# * HTML pages for success and failure that a user will see when they follow
# the link in an address verification email sent during registration:
# 'registration_success.html' and 'registration_failure.html'
#
# * The contents of address verification emails sent when an address is added
# to a Matrix account: 'add_threepid.html' and 'add_threepid.txt'
#
# * HTML pages for success and failure that a user will see when they follow
# the link in an address verification email sent when an address is added
# to a Matrix account: 'add_threepid_success.html' and
# 'add_threepid_failure.html'
#
# You can see the default templates at:
# [Link]
#
#template_dir: "res/templates"
## Push ##
push:
# Clients requesting push notifications can either have the body of
# the message sent in the notification poke along with other details
# like the sender, or just the event ID and room ID (`event_id_only`).
# If clients choose the former, this option controls whether the
# notification request includes the content of the event (other details
# like the sender are still included). For `event_id_only` push, it
# has no effect.
#
# For modern android devices the notification content will still appear
# because it is loaded by the app. iPhone, however will send a
# notification saying only that a message arrived and who it came from.
#
# The default value is "true" to include message details. Uncomment to only
# include the event ID and room ID in push notification payloads.
#
#include_content: false
## Rooms ##
# If enabled, non server admins can only create groups with local parts
# starting with this prefix
#
#group_creation_prefix: "unofficial_"
# Server admins can define a Python module that implements extra rules for
# allowing or denying incoming events. In order to work, this module needs to
# override the methods defined in synapse/events/third_party_rules.py.
#
# This feature is designed to be used in closed federations only, where each
# participating server enforces the same rules.
#
#third_party_event_rules:
# module: "my_custom_project.SuperRulesSet"
# config:
# example_option: 'things'
## Opentracing ##
# The list of homeservers we wish to send and receive span contexts and span
baggage.
# See docs/[Link].
#
# This is a list of regexes which are matched against the server_name of the
# homeserver.
#
# By default, it is empty, so no servers are matched.
#
#homeserver_whitelist:
# - ".*"
# A list of the matrix IDs of users whose requests will always be traced,
# even if the tracing system would otherwise drop the traces due to
# probabilistic sampling.
#
# By default, the list is empty.
#
#force_tracing_for_users:
# - "@user1:server_name"
# - "@user2:server_name"
## Workers ##
# Experimental: When using workers you can define which workers should
# handle event persistence and typing notifications. Any worker
# specified here must also be in the `instance_map`.
#
#stream_writers:
# events: worker1
# typing: worker1
# The worker that is used to run background tasks (e.g. cleaning up expired
# data). If not provided this defaults to the main process.
#
#run_background_tasks_on: worker1
# Configuration for Redis when using workers. This *must* be enabled when
# using workers (unless using old style direct TCP configuration).
#
redis:
# Uncomment the below to enable Redis support.
#
#enabled: true
Note that the contents below are not intended to be copied and used as the basis for a real
[Link]. Instead, if you are starting from scratch, please generate a fresh config using
Synapse by following the instructions in Installation.
# Log configuration for Synapse.
#
# This is a YAML file containing a standard Python logging configuration
# dictionary. See [1] for details on the valid settings.
#
# Synapse also supports structured logging for machine readable logs which can
# be ingested by ELK stacks. See [2] for details.
#
# [1]: [Link]
dictionary-schema
# [2]: [Link]
org/synapse/blob/master/docs/structured_logging.md
version: 1
formatters:
precise:
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s
- %(message)s'
handlers:
file:
class: [Link]
formatter: precise
filename: /var/log/matrix-synapse/[Link]
when: midnight
backupCount: 3 # Does not include the current log file.
encoding: utf8
# Default to buffering writes to log file for efficiency. This means that
# will be a delay for INFO/DEBUG logs to get written, but WARNING/ERROR
# logs will still be flushed immediately.
buffer:
class: [Link]
target: file
# The capacity is the number of log lines that are buffered before
# being written to disk. Increasing this will lead to better
# performance, at the expensive of it taking longer for log lines to
# be written to disk.
capacity: 10
flushLevel: 30 # Flush for WARNING logs as well
# A handler that writes logs to stderr. Unused by default, but can be used
# instead of "buffer" and "file" in the logger handlers.
console:
class: [Link]
formatter: precise
loggers:
[Link]:
# beware: increasing this to DEBUG will make synapse log sensitive
# information such as access tokens.
level: INFO
twisted:
# We send the twisted logging directly to the file handler,
# to work around [Link]
# when using "buffer" logger. Use "console" to log to stderr instead.
handlers: [file]
propagate: false
root:
level: INFO
# Write logs to the `buffer` handler, which will buffer them together in
memory,
# then write them to a file.
#
# Replace "buffer" with "console" to log to stderr instead. (Note that you'll
# also need to update the configuration for the `twisted` logger above, in
# this case.)
#
handlers: [buffer]
disable_existing_loggers: false
``__`
Structured Logging
A structured logging system can be useful when your logs are destined for a machine to parse
and process. By maintaining its machine-readable characteristics, it enables more efficient
searching and aggregations when consumed by software such as the "ELK stack".
Synapse's structured logging system is configured via the file that Synapse's log_config config
option points to. The file should include a formatter which uses the
[Link] class included with Synapse and a handler which uses
the above formatter.
version: 1
formatters:
structured:
class: [Link]
handlers:
file:
class: [Link]
formatter: structured
filename: /path/to/my/logs/[Link]
when: midnight
backupCount: 3 # Does not include the current log file.
encoding: utf8
loggers:
synapse:
level: INFO
handlers: [remote]
[Link]:
level: WARNING
The above logging config will set Synapse as 'INFO' logging level by default, with the SQL layer
at 'WARNING', and will log to a file, stored as JSON.
version: 1
formatters:
structured:
class: [Link]
handlers:
remote:
class: [Link]
formatter: structured
host: [Link]
port: 9999
loggers:
synapse:
level: INFO
handlers: [remote]
[Link]:
level: WARNING
The above logging config will set Synapse as 'INFO' logging level by default, with the SQL layer
at 'WARNING', and will log JSON formatted messages to a remote endpoint at [Link]:9999.
Synapse currently automatically converts the old configuration to the new configuration, but
this will be removed in a future version of Synapse. The following reference can be used to
update your configuration. Based on the drain type , we can pick a new handler:
For each new handler and formatter they should be added to the logging configuration and
then assigned to either a logger or the root logger.
structured: true
loggers:
synapse:
level: INFO
[Link]:
level: WARNING
drains:
console:
type: console
location: stdout
file:
type: file_json
location: [Link]
formatters:
json:
class: [Link]
handlers:
console:
class: [Link]
location: ext://[Link]
file:
class: [Link]
formatter: json
filename: [Link]
loggers:
synapse:
level: INFO
handlers: [console, file]
[Link]:
level: WARNING
The new logging configuration is a bit more verbose, but significantly more flexible. It allows for
configuration that were not previously possible, such as sending plain logs over the network, or
using different handlers for different modules.
User Authentication
Synapse supports multiple methods of authenticating users, either out-of-the-box or through
custom pluggable authentication modules.
Any OP should work with Synapse, as long as it supports the authorization code flow. There are
a few options for that:
start a local OP. Synapse has been tested with Hydra and Dex. Note that for an OP to
work, it should be served under a secure (HTTPS) origin. A certificate signed with a self-
signed, locally trusted CA should work. In that case, start Synapse with a SSL_CERT_FILE
environment variable set to the path of the CA.
set up a SaaS OP, like Google, Auth0 or Okta. Synapse has been tested with Auth0 and
Google.
It may also be possible to use other OAuth2 providers which provide the authorization code
grant type, such as Github.
Preparing Synapse
The OpenID integration in Synapse uses the authlib library, which must be installed as
follows:
The relevant libraries are included in the Docker images and Debian packages provided
by [Link] so no further action is needed.
For other installation mechanisms, see the documentation provided by the maintainer.
To enable the OpenID integration, you should then add a section to the oidc_providers
setting in your configuration file (or uncomment one of the existing examples). See
sample_config.yaml for some sample settings, as well as the text below for example
configurations for specific providers.
Sample configs
Here are a few configs for providers that should work with Synapse.
Azure AD can act as an OpenID Connect Provider. Register a new application under App
registrations in the Azure AD management console. The RedirectURI for your application should
point to your matrix server: [synapse public baseurl]/_synapse/client/oidc/callback
Go to Certificates & secrets and register a new client secret. Make note of your Directory (tenant)
ID as it will be used in the Azure links. Edit your Synapse config file and change the
oidc_config section:
oidc_providers:
- idp_id: microsoft
idp_name: Microsoft
issuer: "[Link] id>/v2.0"
client_id: "<client id>"
client_secret: "<client secret>"
scopes: ["openid", "profile"]
authorization_endpoint: "[Link]
id>/oauth2/v2.0/authorize"
token_endpoint: "[Link]
id>/oauth2/v2.0/token"
userinfo_endpoint: "[Link]
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username.split('@')[0] }}"
display_name_template: "{{ [Link] }}"
Dex
Dex is a simple, open-source, certified OpenID Connect Provider. Although it is designed to help
building a full-blown provider with an external database, it can be configured with static
passwords in a config file.
Edit examples/[Link] config file from the Dex repo to add a client:
staticClients:
- id: synapse
secret: secret
redirectURIs:
- '[synapse public baseurl]/_synapse/client/oidc/callback'
name: 'Synapse'
Synapse config:
oidc_providers:
- idp_id: dex
idp_name: "My Dex server"
skip_verification: true # This is needed as Dex is served on an insecure
endpoint
issuer: "[Link]
client_id: "synapse"
client_secret: "secret"
scopes: ["openid", "profile"]
user_mapping_provider:
config:
localpart_template: "{{ [Link] }}"
display_name_template: "{{ [Link]|capitalize }}"
Keycloak
Follow the Getting Started Guide to install Keycloak and set up a realm.
Field Value
Client ID synapse
3. Click Save
4. Fill in the fields as below:
Field Value
Client ID synapse
Enabled On
Field Value
Client Protocol openid-connect
5. Click Save
6. On the Credentials tab, update the fields:
Field Value
Client Authenticator Client ID and Secret
oidc_providers:
- idp_id: keycloak
idp_name: "My KeyCloak server"
issuer: "[Link]
client_id: "synapse"
client_secret: "copy secret generated from above"
scopes: ["openid", "profile"]
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ [Link] }}"
Auth0
Code sample
Synapse config:
oidc_providers:
- idp_id: auth0
idp_name: Auth0
issuer: "[Link] # TO BE FILLED
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
scopes: ["openid", "profile"]
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ [Link] }}"
GitHub
GitHub is a bit special as it is not an OpenID Connect compliant provider, but just a regular
OAuth2 provider.
The /user API endpoint can be used to retrieve information on the authenticated user. As the
Synapse login mechanism needs an attribute to uniquely identify users, and that endpoint does
not return a sub property, an alternative subject_claim has to be set.
Synapse config:
oidc_providers:
- idp_id: github
idp_name: Github
idp_brand: "github" # optional: styling hint for clients
discover: false
issuer: "[Link]
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
authorization_endpoint: "[Link]
token_endpoint: "[Link]
userinfo_endpoint: "[Link]
scopes: ["read:user"]
user_mapping_provider:
config:
subject_claim: "id"
localpart_template: "{{ [Link] }}"
display_name_template: "{{ [Link] }}"
Google
oidc_providers:
- idp_id: google
idp_name: Google
idp_brand: "google" # optional: styling hint for clients
issuer: "[Link]
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
scopes: ["openid", "profile"]
user_mapping_provider:
config:
localpart_template: "{{ user.given_name|lower }}"
display_name_template: "{{ [Link] }}"
4. Back in the Google console, add this Authorized redirect URI: [synapse public
baseurl]/_synapse/client/oidc/callback .
Twitch
Synapse config:
oidc_providers:
- idp_id: twitch
idp_name: Twitch
issuer: "[Link]
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
client_auth_method: "client_secret_post"
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ [Link] }}"
GitLab
Synapse config:
oidc_providers:
- idp_id: gitlab
idp_name: Gitlab
idp_brand: "gitlab" # optional: styling hint for clients
issuer: "[Link]
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
client_auth_method: "client_secret_post"
scopes: ["openid", "read_user"]
user_profile_method: "userinfo_endpoint"
user_mapping_provider:
config:
localpart_template: '{{ [Link] }}'
display_name_template: '{{ [Link] }}'
Like Github, Facebook provide a custom OAuth2 API rather than an OIDC-compliant one so
requires a little more configuration.
0. You will need a Facebook developer account. You can register for one here.
1. On the apps page of the developer console, "Create App", and choose "Build Connected
Experiences".
2. Once the app is created, add "Facebook Login" and choose "Web". You don't need to go
through the whole form here.
3. In the left-hand menu, open "Products"/"Facebook Login"/"Settings".
Add [synapse public baseurl]/_synapse/client/oidc/callback as an OAuth
Redirect URL.
4. In the left-hand menu, open "Settings/Basic". Here you can copy the "App ID" and "App
Secret" for use below.
Synapse config:
- idp_id: facebook
idp_name: Facebook
idp_brand: "facebook" # optional: styling hint for clients
discover: false
issuer: "[Link]
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
scopes: ["openid", "email"]
authorization_endpoint: [Link]
token_endpoint: [Link]
user_profile_method: "userinfo_endpoint"
userinfo_endpoint: "[Link]
fields=id,name,email,picture"
user_mapping_provider:
config:
subject_claim: "id"
display_name_template: "{{ [Link] }}"
Relevant documents:
[Link]
Using Facebook's Graph API: [Link]
graph-api/
Reference to the User endpoint: [Link]
api/reference/user
Gitea
Gitea is, like Github, not an OpenID provider, but just an OAuth2 provider.
The /user API endpoint can be used to retrieve information on the authenticated user. As the
Synapse login mechanism needs an attribute to uniquely identify users, and that endpoint does
not return a sub property, an alternative subject_claim has to be set.
Synapse config:
oidc_providers:
- idp_id: gitea
idp_name: Gitea
discover: false
issuer: "[Link]
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
client_auth_method: client_secret_post
scopes: [] # Gitea doesn't support Scopes
authorization_endpoint: "[Link]
token_endpoint: "[Link]
userinfo_endpoint: "[Link]
user_mapping_provider:
config:
subject_claim: "id"
localpart_template: "{{ [Link] }}"
display_name_template: "{{ user.full_name }}"
XWiki
Synapse config:
oidc_providers:
- idp_id: xwiki
idp_name: "XWiki"
issuer: "[Link]
client_id: "your-client-id" # TO BE FILLED
client_auth_method: none
scopes: ["openid", "profile"]
user_profile_method: "userinfo_endpoint"
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ [Link] }}"
Apple
Configuring "Sign in with Apple" (SiWA) requires an Apple Developer account.
You will need to create a new "Services ID" for SiWA, and create and download a private key
with "SiWA" enabled.
- idp_id: apple
idp_name: Apple
issuer: "[Link]
client_id: "your-client-id" # Set to the "identifier" for your "ServicesID"
client_auth_method: "client_secret_post"
client_secret_jwt_key:
key_file: "/path/to/AuthKey_KEYIDCODE.p8" # point to your key file
jwt_header:
alg: ES256
kid: "KEYIDCODE" # Set to the 10-char Key ID
jwt_payload:
iss: TEAMIDCODE # Set to the 10-char Team ID
scopes: ["name", "email", "openid"]
authorization_endpoint: [Link]
response_mode=form_post
user_mapping_provider:
config:
email_template: "{{ [Link] }}"
SSO Mapping Providers
A mapping provider is a Python class (loaded via a Python module) that works out how to map
attributes of a SSO response to Matrix-specific user attributes. Details such as user ID localpart,
displayname, and even avatar URLs are all things that can be mapped from talking to a SSO
service.
As an example, a SSO service may return the email address "[Link]@[Link]" for a
user, whereas Synapse will need to figure out how to turn that into a displayname when
creating a Matrix user for this individual. It may choose John Smith , or Smith, John
[[Link]] or any number of variations. As each Synapse configuration may want
something different, this is where SAML mapping providers come into play.
SSO mapping providers are currently supported for OpenID and SAML SSO configurations.
Please see the details below for how to implement your own.
It is up to the mapping provider whether the user should be assigned a predefined Matrix ID
based on the SSO attributes, or if the user should be allowed to choose their own username.
In the first case - where users are automatically allocated a Matrix ID - it is the responsibility of
the mapping provider to normalise the SSO attributes and map them to a valid Matrix ID. The
specification for Matrix IDs has some information about what is considered valid.
If the mapping provider does not assign a Matrix ID, then Synapse will automatically serve an
HTML page allowing the user to pick their own username.
External mapping providers are provided to Synapse in the form of an external Python module.
You can retrieve this module from PyPI or elsewhere, but it must be importable via Synapse
(e.g. it must be in the same virtualenv as Synapse). The Synapse config is then modified to point
to the mapping provider (and optionally provide additional configuration for it).
__init__(self, parsed_config)
Arguments:
parsed_config - A configuration object that is the return value of the
parse_config method. You should set any configuration options needed by
the module here.
parse_config(config)
This method should have the @staticmethod decoration.
Arguments:
config - A dict representing the parsed content of the
oidc_config.user_mapping_provider.config homeserver config option. Runs
on homeserver startup. Providers should extract and validate any option
values they need here.
Whatever is returned will be passed back to the user mapping provider module's
__init__ method during construction.
get_remote_user_id(self, userinfo)
Arguments:
userinfo - A [Link] object to extract user
information from.
This method must return a string, which is the unique, immutable identifier for the
user. Commonly the sub claim of the response.
map_user_attributes(self, userinfo, token, failures)
This method must be async.
Arguments:
userinfo - A [Link] object to extract user
information from.
token - A dictionary which includes information necessary to make further
requests to the OpenID provider.
failures - An int that represents the amount of times the returned mxid
localpart mapping has failed. This should be used to create a deduplicated
mxid localpart which should be returned instead. For example, if this method
returns [Link] as the value of localpart in the returned dict, and that is
already taken on the homeserver, this method will be called again with the
same parameters but with failures=1. The method should then return a
different localpart value, such as john.doe1 .
Returns a dictionary with two keys:
localpart : A string, used to generate the Matrix ID. If this is None , the user is
prompted to pick their own username. This is only used during a user's first
login. Once a localpart has been associated with a remote user ID (see
get_remote_user_id ) it cannot be updated.
displayname : An optional string, the display name for the user.
get_extra_attributes(self, userinfo, token)
Arguments:
Note that care should be taken to not overwrite any of the parameters usually
returned as part of the login response.
Synapse has a built-in OpenID mapping provider if a custom provider isn't specified in the
config. It is located at [Link] .
Arguments:
This method must return a dictionary, which will then be used by Synapse to build a
new user. The following keys are allowed:
mxid_localpart - A string, the mxid localpart of the new user. If this is None ,
the user is prompted to pick their own username. This is only used during a
user's first login. Once a localpart has been associated with a remote user ID
(see get_remote_user_id ) it cannot be updated.
displayname - The displayname of the new user. If not provided, will default to
the value of mxid_localpart .
emails - A list of emails for the new user. If not provided, will default to an
empty list.
Synapse has a built-in SAML mapping provider if a custom provider isn't specified in the config.
It is located at [Link] .
Password auth provider modules
Password auth providers offer a way for server administrators to integrate their Synapse
installation with an existing authentication system.
A password auth provider is a Python class which is dynamically loaded into Synapse, and
provides a number of methods by which it can integrate with the authentication system.
This document serves as a reference for those looking to implement their own password auth
providers. Additionally, here is a list of known password auth provider module
implementations:
matrix-synapse-ldap3
matrix-synapse-shared-secret-auth
matrix-synapse-rest-password-provider
Required methods
Password auth provider classes must provide the following methods:
parse_config(config) This method is passed the config object for this module from
the homeserver configuration file.
It should perform any appropriate sanity checks on the provided configuration, and
return an object which is then passed into __init__ .
Optional methods
Password auth provider classes may optionally provide the following methods:
get_db_schema_files(self)
This method, if implemented, should return an Iterable of (name, stream) pairs of
database schema files. Each file is applied in turn at initialisation, and a record is then
made in the database so that it is not re-applied on the next start.
get_supported_login_types(self)
This method, if implemented, should return a dict mapping from a login type identifier
(such as [Link] ) to an iterable giving the fields which must be provided by the
user in the submission to the /login API. These fields are passed in the login_dict
dictionary to check_auth .
For example, if a password auth provider wants to implement a custom login type of
[Link].custom_login , where the client is expected to pass the fields secret1 and
secret2 , the provider should implement this method and return the following dict:
This method does the real work. If implemented, it will be called for each login attempt
where the login type matches one of the keys returned by get_supported_login_types .
It is passed the (possibly unqualified) user field provided by the client, the login type, and
a dictionary of login secrets passed by the client.
The method should return an Awaitable object, which resolves to the canonical
@localpart:domain user ID if authentication is successful, and None if not.
Alternatively, the Awaitable can resolve to a (str, func) tuple, in which case the
second field is a callback which will be called with the result from the /login call
(including access_token , device_id , etc.)
This method, if implemented, is called when a user attempts to register or log in with a
third party identifier, such as email. It is passed the medium (ex. "email"), an address (ex.
"jdoe@[Link]") and the user's password.
The method should return an Awaitable object, which resolves to a str containing the
user's (canonical) User id if authentication was successful, and None if not.
The method should return an Awaitable object, which resolves to True if authentication
is successful, and False if not.
This method, if implemented, is called when a user logs out. It is passed the qualified user
ID, the ID of the deactivated device (if any: access tokens are occasionally created without
an associated device ID), and the (now deactivated) access token.
It may return an Awaitable object; the logout request will wait for the Awaitable to
complete, but the result is ignored.
JWT Login Type
Synapse comes with a non-standard login type to support JSON Web Tokens. In general the
documentation for the login endpoint is still valid (and the mechanism works similarly to the
token based login).
To log in using a JSON Web Token, clients should submit a /login request as follows:
{
"type": "[Link]",
"token": "<jwt>"
}
Note that the login type of [Link] is supported, but is deprecated. This will be removed
in a future version of Synapse.
The token field should include the JSON web token with the following claims:
The sub (subject) claim is required and should encode the local part of the user ID.
The expiration time ( exp ), not before time ( nbf ), and issued at ( iat ) claims are optional,
but validated if present.
The issuer ( iss ) claim is optional, but required and validated if configured.
The audience ( aud ) claim is optional, but required and validated if configured. Providing
the audience claim when not configured will cause validation to fail.
In the case that the token is not valid, the homeserver must respond with 403 Forbidden and
an error code of M_FORBIDDEN .
As with other login types, there are additional fields (e.g. device_id and
initial_device_display_name ) which can be included in the above request.
Preparing Synapse
The JSON Web Token integration in Synapse uses the PyJWT library, which must be installed as
follows:
The relevant libraries are included in the Docker images and Debian packages provided
by [Link] so no further action is needed.
To enable the JSON web token integration, you should then add an jwt_config section to your
configuration file (or uncomment the enabled: true line in the existing section). See
sample_config.yaml for some sample settings.
1. Configure Synapse with JWT logins, note that this example uses a pre-shared secret and
an algorithm of HS256:
jwt_config:
enabled: true
secret: "my-secret-token"
algorithm: "HS256"
curl [Link]
You should now be able to use the returned access token to query the client API.
Overview
A captcha can be enabled on your homeserver to help prevent bots from registering accounts.
Synapse currently uses Google's reCAPTCHA service which requires API keys from Google.
recaptcha_public_key: YOUR_SITE_KEY
recaptcha_private_key: YOUR_SECRET_KEY
enable_registration_captcha: true
For example:
app_service_config_files:
- /home/matrix/.synapse/<your-AS>.yaml
exclusive : If enabled, only this application service is allowed to register users in its
namespace(s). group_id : All users of this application service are dynamically joined to this
group. This is useful for e.g user organisation or flairs.
See the spec for further details on how application services work.
Server Notices
'Server Notices' are a new feature introduced in Synapse 0.30. They provide a channel whereby
server administrators can send messages to users on the server.
This is a feature specific to Synapse, but it uses standard Matrix communication mechanisms,
so should work with any Matrix client.
User experience
When the user is first sent a server notice, they will get an invitation to a room (typically called
'Server Notices', though this is configurable in [Link] ). They will be unable to reject
this invitation - attempts to do so will receive an error.
Once they accept the invitation, they will see the notice message in the room history; it will
appear to have come from the 'server notices user' (see below).
The user is prevented from sending any messages in this room by the power levels.
Having joined the room, the user can leave the room if they want. Subsequent server notices
will then cause a new room to be created.
Synapse configuration
Server notices come from a specific user id on the server. Server administrators are free to
choose the user id - something like server is suggested, meaning the notices will come from
@server:<your_server_name> . Once the Server Notices user is configured, that user id
becomes a special, privileged user, so administrators should ensure that it is not already
allocated.
The only compulsory setting is system_mxid_localpart , which defines the user id of the Server
Notices user, as above. room_name defines the name of the room which will be created.
Sending notices
To send server notices to users you can use the admin_api.
Support in Synapse for tracking agreement
to server terms and conditions
Synapse 0.30 introduces support for tracking whether users have agreed to the terms and
conditions set by the administrator of a server - and blocking access to the server until they
have.
There are several parts to this functionality; each requires some specific configuration in
[Link] to be enabled.
Note that various parts of the configuation and this document refer to the "privacy policy":
agreement with a privacy policy is one particular use of this feature, but of course
adminstrators can specify other terms and conditions unrelated to "privacy" per se.
To enable this, first create templates for the policy and success pages. These should be stored
on the local filesystem.
These templates use the Jinja2 templating language, and docs/privacy_policy_templates gives
examples of the sort of thing that can be done.
Note that the templates must be stored under a name giving the language of the template -
currently this must always be en (for "English"); internationalisation support is intended for the
future.
The template for the policy itself should be versioned and named according to the version: for
example [Link] . The version of the policy which the user has agreed to is stored in the
database.
Once the templates are in place, make the following changes to [Link] :
template_dir points to the directory containing the policy templates. version defines
the version of the policy which will be served to the user. In the example above, Synapse
will serve privacy_policy_templates/en/[Link] .
This should be set to an arbitrary secret string (try pwgen -y 30 to generate suitable
secrets).
3. Add consent wherever the client resource is currently enabled in the listeners
configuration. For example:
listeners:
- port: 8008
resources:
- names:
- client
- consent
Finally, ensure that jinja2 is installed. If you are using a virtualenv, this should be a matter of
pip install Jinja2 . On debian, try apt-get install python-jinja2 .
Once this is complete, and the server has been restarted, try visiting
[Link] . If correctly configured, this should give an error "Missing
string query parameter 'u'". It is now possible to manually construct URIs where users can give
their consent.
{% if not public_version %}
<!-- The variables used here are only provided when the 'u' param is given
to the homeserver -->
<form method="post" action="consent">
<input type="hidden" name="v" value="{{version}}"/>
<input type="hidden" name="u" value="{{user}}"/>
<input type="hidden" name="h" value="{{userhmac}}"/>
<input type="submit" value="Sure thing!"/>
</form>
{% endif %}
It may be useful to manually construct the "consent URI" for a given user - for instance, in order
to send them an email asking them to consent. To do this, take the base
[Link] URL and add the following query parameters:
u : the user id of the user. This can either be a full MXID ( @user:[Link] ) or just the
localpart ( user ).
user_consent:
server_notice_content:
msgtype: [Link]
body: >-
Please give your consent to the privacy policy at %(consent_uri)s.
Synapse automatically replaces the placeholder %(consent_uri)s with the consent uri for
that user.
ensure that public_baseurl is set in [Link] , and gives the base URI that
clients use to connect to the server. (It is used to construct consent_uri in the server
notice.)
Synapse automatically replaces the placeholder %(consent_uri)s with the consent uri for that
user.
ensure that public_baseurl is set in [Link] , and gives the base URI that clients use
to connect to the server. (It is used to construct consent_uri in the error.)
URL Previews
Design notes on a URL previewing service for Matrix:
Options are:
1. Have an AS which listens for URLs, downloads them, and inserts an event that describes
their metadata.
Pros:
Decouples the implementation entirely from Synapse.
Uses existing Matrix events & content repo to store the metadata.
Cons:
Which AS should provide this service for a room, and why should you trust it?
Doesn't work well with E2E; you'd have to cut the AS into every room
the AS would end up subscribing to every room anyway.
2. Have a generic preview API (nothing to do with Matrix) that provides a previewing service:
Pros:
Simple and flexible; can be used by any clients at any point
Cons:
If each HS provides one of these independently, all the HSes in a room may
needlessly DoS the target URI
We need somewhere to store the URL metadata rather than just using Matrix itself
We can't piggyback on matrix to distribute the metadata between HSes.
3. Make the synapse of the sending user responsible for spidering the URL and inserting an
event asynchronously which describes the metadata.
Pros:
Works transparently for all clients
Piggy-backs nicely on using Matrix for distributing the metadata.
No confusion as to which AS
Cons:
Doesn't work with E2E
We might want to decouple the implementation of the spider from the HS, given
spider behaviour can be quite complicated and evolve much more rapidly than the
HS. It's more like a bot than a core part of the server.
4. Make the sending client use the preview API and insert the event itself when successful.
Pros:
Works well with E2E
No custom server functionality
Lets the client customise the preview that they send (like on FB)
Cons:
Entirely specific to the sending client, whereas it'd be nice if /any/ URL was correctly
previewed if clients support it.
5. Have the option of specifying a shared (centralised) previewing service used by a room, to
avoid all the different HSes in the room DoSing the target.
Sending clients do their best to create and send a preview at the point of sending the
message, perhaps delaying the message until the preview is computed? (This also lets the
user validate the preview before sending)
Receiving clients have the option of going and creating their own preview if one doesn't
arrive soon enough (or if the original sender didn't create one)
This is a bit magical though in that the preview could come from two entirely different sources -
the sending HS or your local one. However, this can always be exposed to users: "Generate
your own URL previews if none are available?"
This is tantamount also to senders calculating their own thumbnails for sending in advance of
the main content - we are trusting the sender not to lie about the content in the thumbnail.
Whereas currently thumbnails are calculated by the receiving homeserver to avoid this attack.
However, this kind of phishing attack does exist whether we let senders pick their thumbnails
or not, in that a malicious sender can send normal text messages around the attachment
claiming it to be legitimate. We could rely on (future) reputation/abuse management to punish
users who phish (be it with bogus metadata or bogus descriptions). Bogus metadata is
particularly bad though, especially if it's avoidable.
As a first cut, let's do #2 and have the receiver hit the API to calculate its own previews (as it
does currently for image thumbnails). We can then extend/optimise this to option 4 as a special
extra if needed.
API
GET /_matrix/media/r0/preview_url?url=[Link]
200 OK
{
"og:type" : "article"
"og:url" :
"[Link]
"og:title" : "Matrix on Twitter"
"og:image" :
"[Link]
"og:description" : "“Synapse 0.12 is out! Lots of polishing, performance
&amp; bugfixes: /sync API, /r0 prefix, fulltext search, 3PID invites
[Link]
"og:site_name" : "Twitter"
}
The directory info is stored in various tables, which can (typically after DB corruption) get stale
or out of sync. If this happens, for now the solution to fix it is to execute the SQL here and then
restart synapse. This should then start a background task to flush the current tables and
regenerate the directory.
Message retention policies
Synapse admins can enable support for message retention policies on their homeserver.
Message retention policies exist at a room level, follow the semantics described in MSC1763,
and allow server and room admins to configure how long messages should be kept in a
homeserver's database before being purged from it. Please note that, as this feature isn't
part of the Matrix specification yet, this implementation is to be considered as
experimental.
A message retention policy is mainly defined by its max_lifetime parameter, which defines
how long a message can be kept around after it was sent to the room. If a room doesn't have a
message retention policy, and there's no default one for a given server, then no message sent
in that room is ever purged on that server.
MSC1763 also specifies semantics for a min_lifetime parameter which defines the amount of
time after which an event can get purged (after it was sent to the room), but Synapse doesn't
currently support it beyond registering it.
Once an event reaches its expiry date (defined as the time it was sent plus the value for
max_lifetime in the room), two things happen:
Since purge jobs don't run continuously, this means that an event might stay in a server's
database for longer than the value for max_lifetime in the room would allow, though hidden
from clients.
Similarly, if a server (with support for message retention policies enabled) receives from
another server an event that should have been purged according to its room's policy, then the
receiving server will process and store that event until it's picked up by the next purge job,
though it will always hide it from clients.
Synapse requires at least one message in each room, so it will never delete the last message in
a room. It will, however, hide it from clients.
Server configuration
Support for this feature can be enabled and configured in the retention section of the
Synapse configuration file (see the sample file).
To enable support for message retention policies, set the setting enabled in this section to
true .
Default policy
A default message retention policy is a policy defined in Synapse's configuration that is used by
Synapse for every room that doesn't have a message retention policy configured in its state.
This allows server admins to ensure that messages are never kept indefinitely in a server's
database.
A default policy can be defined as such, in the retention section of the configuration file:
default_policy:
min_lifetime: 1d
max_lifetime: 1y
Here, min_lifetime and max_lifetime have the same meaning and level of support as
previously described. They can be expressed either as a duration (using the units s (seconds),
m (minutes), h (hours), d (days), w (weeks) and y (years)) or as a number of milliseconds.
Purge jobs
Purge jobs are the jobs that Synapse runs in the background to purge expired events from the
database. They are only run if support for message retention policies is enabled in the server's
configuration. If no configuration for purge jobs is configured by the server admin, Synapse will
use a default configuration, which is described in the sample configuration file.
Some server admins might want a finer control on when events are removed depending on an
event's room's policy. This can be done by setting the purge_jobs sub-section in the
retention section of the configuration file. An example of such configuration could be:
purge_jobs:
- longest_max_lifetime: 3d
interval: 12h
- shortest_max_lifetime: 3d
longest_max_lifetime: 1w
interval: 1d
- shortest_max_lifetime: 1w
interval: 2d
one that runs twice a day (every 12 hours) and purges events in rooms which policy's
max_lifetime is lower or equal to 3 days.
one that runs once a day and purges events in rooms which policy's max_lifetime is
between 3 days and a week.
one that runs once every 2 days and purges events in rooms which policy's max_lifetime
is greater than a week.
Note that this example is tailored to show different configurations and features slightly more
jobs than it's probably necessary (in practice, a server admin would probably consider it better
to replace the two last jobs with one that runs once a day and handles rooms which which
policy's max_lifetime is greater than 3 days).
Keep in mind, when configuring these jobs, that a purge job can become quite heavy on the
server if it targets many rooms, therefore prefer having jobs with a low interval that target a
limited set of rooms. Also make sure to include a job with no minimum and one with no
maximum to make sure your configuration handles every policy.
As previously mentioned in this documentation, while a purge job that runs e.g. every day
means that an expired event might stay in the database for up to a day after its expiry, Synapse
hides expired events from clients as soon as they expire, so the event is not visible to local
users between its expiry date and the moment it gets purged from the server's database.
Lifetime limits
Server admins can set limits on the values of max_lifetime to use when purging old events in
a room. These limits can be defined as such in the retention section of the configuration file:
allowed_lifetime_min: 1d
allowed_lifetime_max: 1y
The limits are considered when running purge jobs. If necessary, the effective value of
max_lifetime will be brought between allowed_lifetime_min and allowed_lifetime_max
(inclusive). This means that, if the value of max_lifetime defined in the room's state is lower
than allowed_lifetime_min , the value of allowed_lifetime_min will be used instead.
Likewise, if the value of max_lifetime is higher than allowed_lifetime_max , the value of
allowed_lifetime_max will be used instead.
In the example above, we ensure Synapse never deletes events that are less than one day old,
and that it always deletes events that are over a year old.
If a default policy is set, and its max_lifetime value is lower than allowed_lifetime_min or
higher than allowed_lifetime_max , the same process applies.
Both parameters are optional; if one is omitted Synapse won't use it to adjust the effective
value of max_lifetime .
Like other settings in this section, these parameters can be expressed either as a duration or as
a number of milliseconds.
Room configuration
To configure a room's message retention policy, a room's admin or moderator needs to send a
state event in that room with the type [Link] and the following content:
{
"max_lifetime": ...
}
In this event's content, the max_lifetime parameter has the same meaning as previously
described, and needs to be expressed in milliseconds. The event's content can also include a
min_lifetime parameter, which has the same meaning and limited support as previously
described.
Note that over every server in the room, only the ones with support for message retention
policies will actually remove expired events. This support is currently not enabled by default in
Synapse.
Using modules
To use a module on Synapse, add it to the modules section of the configuration file:
modules:
- module: my_super_module.MySuperClass
config:
do_thing: true
- module: my_other_super_module.SomeClass
config: {}
Each module is defined by a path to a Python class as well as a configuration. This information
for a given module should be available in the module's own documentation.
Note: When using third-party modules, you effectively allow someone else to run custom code
on your Synapse homeserver. Server admins are encouraged to verify the provenance of the
modules they use on their homeserver and make sure the modules aren't running malicious
code on their instance.
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.
Writing a module
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.
When instantiated, a module is given its parsed configuration as well as an instance of the
synapse.module_api.ModuleApi class. The configuration is a dictionary, and is either the
output of the module's parse_config static method (see below), or the configuration
associated with the module in Synapse's configuration file.
See the documentation for the ModuleApi class here.
@staticmethod
def parse_config(config: dict) -> dict
This method is given a dictionary resulting from parsing the YAML configuration for the
module. It may modify it (for example by parsing durations expressed as strings (e.g. "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.[Link] if not.
Modules can register web resources onto Synapse's web server using the following module API
method:
The path is the full absolute path to register the resource at. For example, if you register a
resource for the path /_synapse/client/my_super_module/say_hello , Synapse will serve it at
http(s)://[HS_URL]/_synapse/client/my_super_module/say_hello . Note that Synapse does
not allow registering resources for several sub-paths in the /_matrix namespace (such as
anything under /_matrix/client for example). It is strongly recommended that modules
register their web resources under the /_synapse/client namespace.
The provided resource is a Python class that implements Twisted's IResource interface (such as
Resource).
Only one resource can be registered for a given path. If several modules attempt to register a
resource for the same path, the module that appears first in Synapse's configuration file takes
priority.
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, and is
under no obligation to implement all callbacks from the categories it registers callbacks for.
To register one of the callbacks described in this section, a module needs to use the module
API's register_spam_checker_callbacks method. The callback functions are passed to
register_spam_checker_callbacks as keyword arguments, with the callback name as the
argument name and the function as its value. This is demonstrated in the example below.
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.
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 (i.e. @alice:[Link] ).
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.
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.
Called when computing search results in the user directory. The module must return a bool
indicating whether the given user profile can appear in search results. The profile 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.
Called when registering a new user. The module must return a RegistrationBehaviour
indicating whether the registration can go through or must be denied, or whether the user may
be allowed to register but will be shadow banned.
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.
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:
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:[Link] , and registers a
web resource to the path /_synapse/client/demo/hello that returns a JSON object.
import json
class DemoResource(Resource):
def __init__(self, config):
super(DemoResource, self).__init__()
[Link] = config
class DemoModule:
def __init__(self, config: dict, api: ModuleApi):
[Link] = config
[Link] = api
[Link].register_web_resource(
path="/_synapse/client/demo/hello",
resource=DemoResource([Link]),
)
[Link].register_spam_checker_callbacks(
user_may_create_room=self.user_may_create_room,
)
@staticmethod
def parse_config(config):
return config
return True
Note: 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.
The spam checking behavior is implemented as a Python class, which must be able to be
imported by the running Synapse.
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 [Link] 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
class ExampleSpamChecker:
def __init__(self, config, api):
[Link] = config
[Link] = api
@staticmethod
def parse_config(config):
return config
Configuration
Modify the spam_checker section of your [Link] in the following manner:
Example
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.
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.
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
get_users_for_states
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 [Link] instances.
Synapse will then attempt to send the specified presence updates to each user when possible.
get_interested_users
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
[Link].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:[Link] is passed to this method, and the Set
{"@bob:[Link]", "@charlie:[Link]"} is returned, this signifies that Alice should
receive presence updates sent by Bob and Charlie, regardless of whether these users share a
room.
Example
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]
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")
return config
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]
return destination_users
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
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.
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:
routing_module:
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:[Link]"]
blacklisted_users:
- "@alice:[Link]"
- "@bob:[Link]"
...
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 .
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 out functionality into
multiple separate python processes. These processes are called 'workers', and are (eventually)
intended to scale horizontally independently.
Synapse's worker support is under active development and subject to change as we attempt to
rapidly scale ever larger Synapse instances. However we are documenting it here to help
admins needing a highly scalable Synapse instance similar to the one running [Link] .
All processes continue to share the same database instance, and as such, workers only work
with PostgreSQL-based Synapse deployments. SQLite should only be used for demo purposes
and any admin considering workers should already be running PostgreSQL.
When configured to do so, Synapse uses a Redis pub/sub channel to send the replication
stream between all configured Synapse processes. Additionally, processes may make HTTP
requests to each other, primarily for operations which need to wait for a reply ─ such as
sending an event.
Redis support was added in v1.13.0 with it becoming the recommended method in v1.18.0. It
replaced the old direct TCP connections (which is deprecated as of v1.18.0) to the main process.
With Redis, rather than all the workers connecting to the main process, all the workers and the
main process connect to Redis, which relays replication commands between processes. This
can give a significant cpu saving on the main process and will be a prerequisite for upcoming
performance improvements.
If Redis support is enabled Synapse will use it as a shared cache, as well as a pub/sub
mechanism.
See the Architectural diagram section at the end for a visualisation of what this looks like.
Setting up workers
A Redis server is required to manage the communication between the processes. The Redis
server should be installed following the normal procedure for your distribution (e.g. apt
install redis-server on Debian). It is safe to use an existing Redis deployment if you have
one.
Once installed, check that Redis is running and accessible from the host running Synapse, for
example by executing echo PING | nc -q1 localhost 6379 and seeing a response of +PONG .
The appropriate dependencies must also be installed for Synapse. If using a virtualenv, these
can be installed with:
Note that these dependencies are included when synapse is installed with pip install
matrix-synapse[all] . They are also included in the debian packages from [Link] and in
the docker images at [Link]
To make effective use of the workers, you will need to configure an HTTP reverse-proxy such as
nginx or haproxy, which will direct incoming requests to the correct worker, or to the main
synapse instance. See reverse_proxy.md for information on setting up a reverse proxy.
When using workers, each worker process has its own configuration file which contains settings
specific to that worker, such as the HTTP listener that it provides (if any), logging configuration,
etc.
Normally, the worker processes are configured to read from a shared configuration file as well
as the worker-specific configuration files. This makes it easier to keep common configuration
settings synchronised across all the processes.
The main process is somewhat special in this respect: it does not normally need its own
configuration file and can take all of its configuration from the shared configuration file.
Shared configuration
Normally, only a couple of changes are needed to make an existing configuration file suitable
for use with workers. First, you need to enable an "HTTP replication listener" for the main
process; and secondly, you need to enable redis-based replication. Optionally, a shared secret
can be used to authenticate HTTP traffic between workers. For example:
# extend the existing `listeners` section. This defines the ports that the
# main process will listen on.
listeners:
# The HTTP replication port
- port: 9093
bind_address: '[Link]'
type: http
resources:
- names: [replication]
redis:
enabled: true
See the sample config for the full documentation of each option.
Under no circumstances should the replication listener be exposed to the public internet; it
has no authentication and is unencrypted.
Worker configuration
In the config file for each worker, you must specify the type of worker application
( worker_app ), and you should specify a unique name for the worker ( worker_name ). The
currently available worker applications are listed below. You must also specify the HTTP
replication endpoint that it should talk to on the main synapse process.
worker_replication_host should specify the host of the main synapse and
worker_replication_http_port should point to the HTTP replication port. If the worker will
handle HTTP requests then the worker_listeners option should be set with a http listener,
in the same way as the listeners option in the shared config.
For example:
worker_app: [Link].generic_worker
worker_name: worker1
worker_listeners:
- type: http
port: 8083
resources:
- names:
- client
- federation
worker_log_config: /home/matrix/synapse/config/worker1_log_config.yaml
...is a full configuration for a generic worker instance, which will expose a plain HTTP endpoint
on port 8083 separately serving various endpoints, e.g. /sync , which are listed below.
Obviously you should configure your reverse-proxy to route the relevant endpoints to the
worker ( localhost:8083 in the above example).
Finally, you need to start your worker processes. This can be done with either synctl or your
distribution's preferred service manager such as systemd . We recommend the use of systemd
where available: for information on setting up systemd to start synapse workers, see systemd-
with-workers. To use synctl , see synctl_workers.md.
[Link].generic_worker
This worker can handle API requests matching the following regular expressions:
# Sync requests
^/_matrix/client/(v2_alpha|r0)/sync$
^/_matrix/client/(api/v1|v2_alpha|r0)/events$
^/_matrix/client/(api/v1|r0)/initialSync$
^/_matrix/client/(api/v1|r0)/rooms/[^/]+/initialSync$
# Federation requests
^/_matrix/federation/v1/event/
^/_matrix/federation/v1/state/
^/_matrix/federation/v1/state_ids/
^/_matrix/federation/v1/backfill/
^/_matrix/federation/v1/get_missing_events/
^/_matrix/federation/v1/publicRooms
^/_matrix/federation/v1/query/
^/_matrix/federation/v1/make_join/
^/_matrix/federation/v1/make_leave/
^/_matrix/federation/v1/send_join/
^/_matrix/federation/v2/send_join/
^/_matrix/federation/v1/send_leave/
^/_matrix/federation/v2/send_leave/
^/_matrix/federation/v1/invite/
^/_matrix/federation/v2/invite/
^/_matrix/federation/v1/query_auth/
^/_matrix/federation/v1/event_auth/
^/_matrix/federation/v1/exchange_third_party_invite/
^/_matrix/federation/v1/user/devices/
^/_matrix/federation/v1/get_groups_publicised$
^/_matrix/key/v2/query
# Registration/login requests
^/_matrix/client/(api/v1|r0|unstable)/login$
^/_matrix/client/(r0|unstable)/register$
# Event sending requests
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/redact
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/send
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/state/
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/(join|invite|leave|ban|unban|kick)$
^/_matrix/client/(api/v1|r0|unstable)/join/
^/_matrix/client/(api/v1|r0|unstable)/profile/
Additionally, the following REST endpoints can be handled for GET requests:
^/_matrix/federation/v1/groups/
Pagination requests can also be handled, but all requests for a given room must be routed to
the same instance. Additionally, care must be taken to ensure that the purge history admin API
is not used while pagination requests for the room are in flight:
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/messages$
Additionally, the following endpoints should be included if Synapse is configured to use SSO
(you only need to include the ones for whichever SSO provider you're using):
# SAML requests.
^/_synapse/client/saml2/authn_response$
# CAS requests.
^/_matrix/client/(api/v1|r0|unstable)/login/cas/ticket$
Ensure that all SSO logins go to a single process. For multiple workers not handling the SSO
endpoints properly, see #7530 and #9427.
Note that a HTTP listener with client and federation resources must be configured in the
worker_listeners option in the worker config.
Load balancing
It is possible to run multiple instances of this worker app, with incoming requests being load-
balanced between them by the reverse-proxy. However, different endpoints have different
characteristics and so admins may wish to run multiple groups of workers handling different
endpoints so that load balancing can be done in different ways.
For /sync and /initialSync requests it will be more efficient if all requests from a particular
user are routed to a single instance. Extracting a user ID from the access token or
Authorization header is currently left as an exercise for the reader. Admins may additionally
wish to separate out /sync requests that have a since query parameter from those that
don't (and /initialSync ), as requests that don't are known as "initial sync" that happens
when a user logs in on a new device and can be very resource intensive, so isolating these
requests will stop them from interfering with other users ongoing syncs.
Federation and client requests can be balanced via simple round robin.
Registration/login requests can be handled separately purely to help ensure that unexpected
load doesn't affect new logins and sign ups.
Finally, event sending requests can be balanced by the room ID in the URI (or the full URI, or
even just round robin), the room ID is the path component after /rooms/ . If there is a large
bridge connected that is sending or may send lots of events, then a dedicated set of workers
can be provisioned to limit the effects of bursts of events from that bridge on events sent by
normal users.
Stream writers
Additionally, there is experimental support for moving writing of specific streams (such as
events) off of the main process to a particular worker. (This is only supported with Redis-based
replication.)
To enable this, the worker must have a HTTP replication listener configured, have a
worker_name and be listed in the instance_map config. For example to move event
persistence off to a dedicated worker, the shared configuration would include:
instance_map:
event_persister1:
host: localhost
port: 8034
stream_writers:
events: event_persister1
The events stream also experimentally supports having multiple writers, where work is
sharded between them by room ID. Note that you must restart all worker instances when
adding or removing event persisters. An example stream_writers configuration with multiple
writers:
stream_writers:
events:
- event_persister1
- event_persister2
Background tasks
There is also experimental support for moving background tasks to a separate worker.
Background tasks are run periodically or started via replication. Exactly which tasks are
configured to run depends on your Synapse configuration (e.g. if stats is enabled).
To enable this, the worker must have a worker_name and can be configured to run background
tasks. For example, to move background tasks to a dedicated worker, the shared configuration
would include:
run_background_tasks_on: background_worker
[Link]
Handles sending push notifications to sygnal and email. Doesn't handle any REST endpoints
itself, but you should set start_pushers: False in the shared configuration file to stop the
main synapse sending push notifications.
To run multiple instances at once the pusher_instances option should list all pusher instances
by their worker name, e.g.:
pusher_instances:
- pusher_worker1
- pusher_worker2
[Link]
Handles sending output traffic to Application Services. Doesn't handle any REST endpoints
itself, but you should set notify_appservices: False in the shared configuration file to stop
the main synapse sending appservice notifications.
Note this worker cannot be load-balanced: only one instance should be active.
[Link].federation_sender
Handles sending federation traffic to other servers. Doesn't handle any REST endpoints itself,
but you should set send_federation: False in the shared configuration file to stop the main
synapse sending this traffic.
If running multiple federation senders then you must list each instance in the
federation_sender_instances option by their worker_name . All instances must be stopped
and started when adding or removing instances. For example:
federation_sender_instances:
- federation_sender1
- federation_sender2
[Link].media_repository
Handles the media repository. It can handle all endpoints starting with:
/_matrix/media/
... and the following regular expressions matching media-specific administration APIs:
^/_synapse/admin/v1/purge_media_cache$
^/_synapse/admin/v1/room/.*/media.*$
^/_synapse/admin/v1/user/.*/media.*$
^/_synapse/admin/v1/media/.*$
^/_synapse/admin/v1/quarantine_media/.*$
You should also set enable_media_repo: False in the shared configuration file to stop the
main synapse running background jobs related to managing the media repository.
In the media_repository worker configuration file, configure the http listener to expose the
media resource. For example:
worker_listeners:
- type: http
port: 8085
resources:
- names:
- media
Note that if running multiple media repositories they must be on the same server and you
must configure a single instance to run the background tasks, e.g.:
media_instance_running_background_jobs: "media-repository-1"
Note that if a reverse proxy is used , then /_matrix/media/ must be routed for both inbound
client and federation requests (if they are handled separately).
[Link].user_dir
Handles searches in the user directory. It can handle REST endpoints matching the following
regular expressions:
^/_matrix/client/(api/v1|r0|unstable)/user_directory/search$
When using this worker you must also set update_user_directory: False in the shared
configuration file to stop the main synapse running background jobs related to updating the
user directory.
[Link].frontend_proxy
Proxies some frequently-requested client endpoints to add caching and remove load from the
main synapse. It can handle REST endpoints matching the following regular expressions:
^/_matrix/client/(api/v1|r0|unstable)/keys/upload
If use_presence is False in the homeserver config, it can also handle REST endpoints matching
the following regular expressions:
^/_matrix/client/(api/v1|r0|unstable)/presence/[^/]+/status
This "stub" presence handler will pass through GET request but make the PUT effectively a no-
op.
It will proxy any requests it cannot handle to the main synapse instance. It must therefore be
configured with the location of the main instance, via the worker_main_http_uri setting in the
frontend_proxy worker configuration file. For example:
worker_main_http_uri: [Link]
Historical apps
Note: Historically there used to be more apps, however they have been amalgamated into a
single [Link].generic_worker app. The remaining apps are ones that do specific
processing unrelated to requests, e.g. the pusher that handles sending out push notifications
for new events. The intention is for all these to be folded into the generic_worker app and to
use config to define which processes handle the various proccessing such as push notifications.
To migrate to Redis add the redis config as above, and optionally remove the TCP
replication listener from master and worker_replication_port from worker config.
Architectural diagram
The following shows an example setup using Redis and a reverse proxy:
Clients & Federation
|
v
+-----------+
| |
| Reverse |
| Proxy |
| |
+-----------+
| | |
| | | HTTP requests
+-------------------+ | +-----------+
| +---+ |
| | |
v v v
+--------------+ +--------------+ +--------------+ +--------------+
| Main | | Generic | | Generic | | Event |
| Process | | Worker 1 | | Worker 2 | | Persister |
+--------------+ +--------------+ +--------------+ +--------------+
^ ^ | ^ | | ^ | ^ ^
| | | | | | | | | |
| | | | | HTTP | | | | |
| +----------+<--|---|---------+ | | | |
| | +-------------|-->+----------+ |
| | | |
| | | |
v v v v
====================================================================
Redis pub/sub channel
Using synctl with workers
If you want to use synctl to manage your synapse processes, you will need to create an an
additional configuration file for the main synapse process. That configuration should look like
this:
worker_app: [Link]
Additionally, each worker app must be configured with the name of a "pid file", to which it will
write its process ID when it starts. For example, for a synchrotron, you might write:
worker_pid_file: /home/matrix/synapse/[Link]
Finally, to actually run your worker-based synapse, you must pass synctl the -a commandline
option to tell it to operate on all the worker configurations found in the given directory, e.g.:
Currently one should always restart all workers when restarting or upgrading synapse, unless
you explicitly know it's safe not to. For instance, restarting synapse without restarting all the
synchrotrons may result in broken typing notifications.
The folder workers contains an example configuration for the federation_reader worker.
Systemd manages daemonization itself, so ensure that none of the configuration files set either
daemonize or worker_daemonize .
There is no need for a separate configuration file for the master process.
Set up
1. Adjust synapse configuration files as above.
2. Copy the *.service and *.target files in system to /etc/systemd/system .
3. Run systemctl daemon-reload to tell systemd to load the new unit files.
4. Run systemctl enable [Link] . This will configure the synapse master
process to be started as part of the [Link] target.
5. For each worker process to be enabled, run systemctl enable matrix-synapse-
worker@<worker_name>.service . For each <worker_name> , there should be a
corresponding configuration file. /etc/matrix-synapse/workers/<worker_name>.yaml .
6. Start all the synapse processes with systemctl start [Link] .
7. Tell systemd to start synapse on boot with systemctl enable [Link] .
Usage
Once the services are correctly set up, you can use the following commands to manage your
synapse installation:
Hardening
Optional: If further hardening is desired, the file [Link] may be copied
from contrib/systemd/[Link] in this repository to the location
/etc/systemd/system/[Link].d/[Link] (the directory
may have to be created). It enables certain sandboxing features in systemd to further secure
the synapse service. You may read the comments to understand what the override file is doing.
The same file will need to be copied to /etc/systemd/system/matrix-synapse-
worker@.service.d/[Link] (this directory may also have to be
created) in order to apply the same hardening options to any worker processes.
Once these files have been copied to their appropriate locations, simply reload systemd's
manager config files and restart all Synapse services to apply the hardening options. They will
automatically be applied at every restart as long as the override files are present at the
specified locations.
systemctl daemon-reload
# Restart services
systemctl restart [Link]
In order to see their effect, you may run systemd-analyze security [Link]
before and after applying the hardening options to see the changes being applied at a glance.
Administration
This section contains information on managing your Synapse homeserver. This includes:
A user can be marked as a server admin by updating the database directly, e.g.:
A new server admin user can also be created using the register_new_matrix_user command.
This is a script that is located in the scripts/ directory, or possibly already on your $PATH
depending on how Synapse was installed.
Finding your user's access_token is client-dependent, but will usually be shown in the client's
settings.
For more details on access tokens in Matrix, please refer to the complete matrix spec
documentation.
Account validity API
This API allows a server administrator to manage the validity of an account. To use it, you must
enable the account validity feature (under account_validity ) in Synapse's configuration.
Renew account
This API extends the validity of an account by as much time as configured in the period
parameter from the account_validity configuration.
POST /_synapse/admin/v1/account_validity/validity
{
"user_id": "<user ID for the account to renew>",
"expiration_ts": 0,
"enable_renewal_emails": true
}
expiration_ts is an optional parameter and overrides the expiration date, which otherwise
defaults to now + validity period.
The API returns with the new expiration date for this account, as a timestamp in milliseconds
since epoch:
{
"expiration_ts": 0
}
Delete a local group
This API lets a server admin delete a local group. Doing so will kick all users out of the group so
that their clients will correctly handle the group being deleted.
POST /_synapse/admin/v1/delete_group/<group_id>
To use it, you will need to authenticate by providing an access_token for a server admin: see
Admin API.
Show reported events
This API returns information about reported events.
GET /_synapse/admin/v1/event_reports?from=0&limit=10
To use it, you will need to authenticate by providing an access_token for a server admin: see
Admin API.
{
"event_reports": [
{
"event_id": "$bNUFCwGzWca1meCGkjp-zwslF-GfVcXukvRLI1_FaVY",
"id": 2,
"reason": "foo",
"score": -100,
"received_ts": 1570897107409,
"canonical_alias": "#alias1:[Link]",
"room_id": "!ERAgBpSOcCCuTJqQPk:[Link]",
"name": "Matrix HQ",
"sender": "@foobar:[Link]",
"user_id": "@foo:[Link]"
},
{
"event_id": "$3IcdZsDaN_En-S1DF4EMCy3v4gNRKeOJs8W5qTOKj4I",
"id": 3,
"reason": "bar",
"score": -100,
"received_ts": 1598889612059,
"canonical_alias": "#alias2:[Link]",
"room_id": "!eGvUQuTCkHGVwNMOjv:[Link]",
"name": "Your room name here",
"sender": "@foobar:[Link]",
"user_id": "@bar:[Link]"
}
],
"next_token": 2,
"total": 4
}
To paginate, check for next_token and if present, call the endpoint again with from set to the
value of next_token . This will return a new page.
If the endpoint does not return a next_token then there are no more reports to paginate
through.
URL parameters:
limit : integer - Is optional but is used for pagination, denoting the maximum number of
items to return in this call. Defaults to 100 .
from : integer - Is optional but used for pagination, denoting the offset in the returned
results. This should be treated as an opaque value and not explicitly set to anything other
than the return value of next_token from a previous call. Defaults to 0 .
dir : string - Direction of event report order. Whether to fetch the most recent first ( b ) or
the oldest first ( f ). Defaults to b .
user_id : string - Is optional and filters to only return users with user IDs that contain this
value. This is the user who reported the event and wrote the reason.
room_id : string - Is optional and filters to only return rooms with room IDs that contain
this value.
Response
GET /_synapse/admin/v1/event_reports/<report_id>
To use it, you will need to authenticate by providing an access_token for a server admin: see
Admin API.
URL parameters:
Response
The following fields are returned in the JSON response body:
Querying media
These APIs allow extracting media information from the homeserver.
GET /_synapse/admin/v1/room/<room_id>/media
To use it, you will need to authenticate by providing an access_token for a server admin: see
Admin API.
Quarantine media
Quarantining media means that it is marked as inaccessible by users. It applies to any local
media, and any locally-cached copies of remote media.
The media file itself (and any thumbnails) is not deleted from the server.
Quarantining media by ID
This API quarantines a single piece of local or remote media.
Request:
POST /_synapse/admin/v1/media/quarantine/<server_name>/<media_id>
{}
Response:
{}
Remove media from quarantine by ID
This API removes a single piece of local or remote media from quarantine.
Request:
POST /_synapse/admin/v1/media/unquarantine/<server_name>/<media_id>
{}
Response:
{}
Request:
POST /_synapse/admin/v1/room/<room_id>/media/quarantine
{}
Response:
{
"num_quarantined": 10
}
Request:
POST /_synapse/admin/v1/user/<user_id>/media/quarantine
{}
URL Parameters
Response:
{
"num_quarantined": 10
}
Request:
POST /_synapse/admin/v1/media/protect/<media_id>
{}
Response:
{}
Unprotecting media from being quarantined
This API reverts the protection of a media.
Request:
POST /_synapse/admin/v1/media/unprotect/<media_id>
{}
Response:
{}
Request:
DELETE /_synapse/admin/v1/media/<server_name>/<media_id>
{}
URL Parameters
Response:
{
"deleted_media": [
"abcdefghijklmnopqrstuvwx"
],
"total": 1
}
POST /_synapse/admin/v1/media/<server_name>/delete?before_ts=<before_ts>
{}
URL Parameters
Response:
{
"deleted_media": [
"abcdefghijklmnopqrstuvwx",
"abcdefghijklmnopqrstuvwz"
],
"total": 2
}
POST /_synapse/admin/v1/purge_media_cache?before_ts=<unix_timestamp_in_ms>
{}
URL Parameters
Response:
{
"deleted": 10
}
To use it, you will need to authenticate by providing an access_token for a server admin: see
Admin API.
If the user re-requests purged remote media, synapse will re-request the media from the
originating server.
Purge History API
The purge history API allows server admins to purge historic events from their database,
reclaiming disk space.
Depending on the amount of history being purged a call to the API may take several minutes or
longer. During this period users will not be able to paginate further back in the room from the
point being purged from.
Note that Synapse requires at least one message in each room, so it will never delete the last
message in a room.
POST /_synapse/admin/v1/purge_history/<room_id>[/<event_id>]
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
By default, events sent by local users are not deleted, as they may represent the only copies of
this content in existence. (Events sent by remote users are deleted.)
{
"delete_local_events": true
}
The caller must specify the point in the room to purge up to. This can be specified by including
an event_id in the URI, or by setting a purge_up_to_event_id or purge_up_to_ts in the
request body. If an event id is given, that event (and others at the same graph depth) will be
retained. If purge_up_to_ts is given, it should be a timestamp since the unix epoch, in
milliseconds.
The API starts the purge running, and returns immediately with a JSON body with a purge id:
{
"purge_id": "<opaque id>"
}
Purge status query
It is possible to poll for updates on recent purges with a second API;
GET /_synapse/admin/v1/purge_history_status/<purge_id>
Again, you will need to authenticate by providing an access_token for a server admin.
{
"status": "active"
}
[Link]
Deprecated: Purge room API
The old Purge room API is deprecated and will be removed in a future release. See the
new Delete Room API for more details.
This API will remove all trace of a room from your database.
All local users must have left the room before it can be removed.
POST /_synapse/admin/v1/purge_room
{
"room_id": "!room:id"
}
To authenticate yourself to the server, you will need both the shared secret
( registration_shared_secret in the homeserver configuration), and a one-time nonce. If the
registration shared secret is not configured, this API is not enabled.
To fetch the nonce, you need to request one from the API:
Once you have the nonce, you can make a POST to the same URL with a JSON body containing
the nonce, username, password, whether they are an admin (optional, False by default), and a
HMAC digest of the content. Also you can set the displayname (optional, username by default).
As an example:
< {
"access_token": "token_here",
"user_id": "@pepper_roni:localhost",
"home_server": "test",
"device_id": "device_id_here"
}
The MAC is the hex digest output of the HMAC-SHA1 algorithm, with the key being the shared
secret and the content being the nonce, user, password, either the string "admin" or
"notadmin", and optionally the user_type each separated by NULs. For an example of
generation in Python:
import hmac, hashlib
mac = [Link](
key=shared_secret,
digestmod=hashlib.sha1,
)
[Link]([Link]('utf8'))
[Link](b"\x00")
[Link]([Link]('utf8'))
[Link](b"\x00")
[Link]([Link]('utf8'))
[Link](b"\x00")
[Link](b"admin" if admin else b"notadmin")
if user_type:
[Link](b"\x00")
[Link](user_type.encode('utf8'))
return [Link]()
Edit Room Membership API
This API allows an administrator to join an user account with a given user_id to a room with a
given room_id_or_alias . You can only modify the membership of local users. The server
administrator must be in the room and have permission to invite users.
Parameters
The following parameters are available:
Usage
POST /_synapse/admin/v1/join/<room_id_or_alias>
{
"user_id": "@user:[Link]"
}
To use it, you will need to authenticate by providing an access_token for a server admin: see
Admin API.
Response:
{
"room_id": "!636q39766251:[Link]"
}
Contents
List Room API
Parameters
Usage
Room Details API
Room Members API
Room State API
Delete Room API
Parameters
Response
Undoing room shutdowns
Make Room Admin API
Forward Extremities Admin API
Event Context API
Parameters
The following query parameters are available:
Usage
A standard request with no filtering:
GET /_synapse/admin/v1/rooms
{}
Response:
{
"rooms": [
{
"room_id": "!OGEhHVWSdvArJzumhm:[Link]",
"name": "Matrix HQ",
"canonical_alias": "#matrix:[Link]",
"joined_members": 8326,
"joined_local_members": 2,
"version": "1",
"creator": "@foo:[Link]",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
},
... (8 hidden items) ...
{
"room_id": "!xYvNcQPhnkrdUmYczI:[Link]",
"name": "This Week In Matrix (TWIM)",
"canonical_alias": "#twim:[Link]",
"joined_members": 314,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:[Link]",
"encryption": "[Link]-sha2",
"federatable": true,
"public": false,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8345
}
],
"offset": 0,
"total_rooms": 10
}
GET /_synapse/admin/v1/rooms?search_term=TWIM
{}
Response:
{
"rooms": [
{
"room_id": "!xYvNcQPhnkrdUmYczI:[Link]",
"name": "This Week In Matrix (TWIM)",
"canonical_alias": "#twim:[Link]",
"joined_members": 314,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:[Link]",
"encryption": "[Link]-sha2",
"federatable": true,
"public": false,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8
}
],
"offset": 0,
"total_rooms": 1
}
GET /_synapse/admin/v1/rooms?order_by=size
{}
Response:
{
"rooms": [
{
"room_id": "!OGEhHVWSdvArJzumhm:[Link]",
"name": "Matrix HQ",
"canonical_alias": "#matrix:[Link]",
"joined_members": 8326,
"joined_local_members": 2,
"version": "1",
"creator": "@foo:[Link]",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
},
... (98 hidden items) ...
{
"room_id": "!xYvNcQPhnkrdUmYczI:[Link]",
"name": "This Week In Matrix (TWIM)",
"canonical_alias": "#twim:[Link]",
"joined_members": 314,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:[Link]",
"encryption": "[Link]-sha2",
"federatable": true,
"public": false,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8345
}
],
"offset": 0,
"total_rooms": 150
"next_token": 100
}
The presence of the next_token parameter tells us that there are more rooms than returned
in this request, and we need to make another request to get them. To get the next batch of
room results, we repeat our request, setting the from parameter to the value of next_token .
GET /_synapse/admin/v1/rooms?order_by=size&from=100
{}
Response:
{
"rooms": [
{
"room_id": "!mscvqgqpHYjBGDxNym:[Link]",
"name": "Music Theory",
"canonical_alias": "#musictheory:[Link]",
"joined_members": 127,
"joined_local_members": 2,
"version": "1",
"creator": "@foo:[Link]",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
},
... (48 hidden items) ...
{
"room_id": "!twcBhHVdZlQWuuxBhN:[Link]",
"name": "weechat-matrix",
"canonical_alias": "#weechat-matrix:[Link]",
"joined_members": 137,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:[Link]",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8345
}
],
"offset": 100,
"prev_batch": 0,
"total_rooms": 150
}
Once the next_token parameter is no longer present, we know we've reached the end of the
list.
Usage
A standard request:
GET /_synapse/admin/v1/rooms/<room_id>
{}
Response:
{
"room_id": "!mscvqgqpHYjBGDxNym:[Link]",
"name": "Music Theory",
"avatar": "mxc://[Link]/AQDaVFlbkQoErdOgqWRgiGSV",
"topic": "Theory, Composition, Notation, Analysis",
"canonical_alias": "#musictheory:[Link]",
"joined_members": 127,
"joined_local_members": 2,
"joined_local_devices": 2,
"version": "1",
"creator": "@foo:[Link]",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
}
members - A list of all the members that are present in the room, represented by their ids.
total - Total number of members in the room.
Usage
A standard request:
GET /_synapse/admin/v1/rooms/<room_id>/members
{}
Response:
{
"members": [
"@foo:[Link]",
"@bar:[Link]",
"@foobar:[Link]"
],
"total": 3
}
Usage
A standard request:
GET /_synapse/admin/v1/rooms/<room_id>/state
{}
Response:
{
"state": [
{"type": "[Link]", "state_key": "", "etc": true},
{"type": "[Link].power_levels", "state_key": "", "etc": true},
{"type": "[Link]", "state_key": "", "etc": true}
]
}
Shuts down a room. Moves all local users and room aliases automatically to a new room if
new_room_user_id is set. Otherwise local users only leave the room without any information.
The new room will be created with the user specified by the new_room_user_id parameter as
room administrator and will contain a message explaining what happened. Users invited to the
new room will have power level -10 by default, and thus be unable to speak.
This API will remove all trace of the old room from your database after removing all local users.
If purge is true (the default), all traces of the old room will be removed from your database
after removing all local users. If you do not want this to happen, set purge to false .
Depending on the amount of history being purged a call to the API may take several minutes or
longer.
The local server will only have the power to move local user and room aliases to the new room.
Users on other servers will be unaffected.
DELETE /_synapse/admin/v1/rooms/<room_id>
{
"new_room_user_id": "@someuser:[Link]",
"room_name": "Content Violation Notification",
"message": "Bad Room has been shutdown due to content violations on this
server. Please review our Terms of Service.",
"block": true,
"purge": true
}
To use it, you will need to authenticate by providing an access_token for a server admin: see
Admin API.
{
"kicked_users": [
"@foobar:[Link]"
],
"failed_to_kick_users": [],
"local_aliases": [
"#badroom:[Link]",
"#evilsaloon:[Link]"
],
"new_room_id": "!newroomid:[Link]"
}
Parameters
The following parameters should be set in the URL:
new_room_user_id - Optional. If set, a new room will be created with this user ID as the
creator and admin, and all users in the old room will be moved into that room. If not set,
no new room will be created and the users will just be removed from the old room. The
user ID must be on the local server, but does not necessarily have to belong to a
registered user.
room_name - Optional. A string representing the name of the room that new users will be
invited to. Defaults to Content Violation Notification
message - Optional. A string containing the first message that will be sent as
new_room_user_id in the new room. Ideally this will clearly convey why the original room
was shut down. Defaults to Sharing illegal content on this server is not
permitted and rooms in violation will be blocked.
block - Optional. If set to true , this room will be added to a blocking list, preventing
future attempts to join the room. Defaults to false .
purge - Optional. If set to true , it will remove all traces of the room from your database.
Defaults to true .
force_purge - Optional, and ignored unless purge is true . If set to true , it will force a
purge to go ahead even if there are local users still in the room. Do not use this unless a
regular purge operation fails, as it could leave those users' clients in a confused state.
The JSON body must not be empty. The body must be at least {} .
Response
The following fields are returned in the JSON response body:
First, it's important to understand that a room shutdown is very destructive. Undoing a
shutdown is not as simple as pretending it never happened - work has to be done to move
forward instead of resetting the past. In fact, in some cases it might not be possible to recover
at all:
With all that being said, if you still want to try and recover the room:
Aliases that would have been redirected to the Content Violation room.
Users that would have been booted from the room (and will have been force-joined to the
Content Violation room).
Removal of the Content Violation room if desired.
Deprecated endpoint
The previous deprecated API will be removed in a future release, it was:
POST /_synapse/admin/v1/rooms/<room_id>/delete
It behaves the same way than the current endpoint except the path and the method.
Make Room Admin API
Grants another user the highest power available to a local user who is in the room. If the user is
not in the room, and it is not publicly joinable, then invite the user.
By default the server admin (the caller) is granted power, but another user can optionally be
specified, e.g.:
POST /_synapse/admin/v1/rooms/<room_id_or_alias>/make_room_admin
{
"user_id": "@foo:[Link]"
}
GET /_synapse/admin/v1/rooms/<room_id_or_alias>/forward_extremities
{
"count": 1,
"results": [
{
"event_id": "$M5SP266vsnxctfwFgFLNceaCo3ujhRtg_NiiHabcdefgh",
"state_group": 439,
"depth": 123,
"received_ts": 1611263016761
}
]
}
Deleting forward extremities
WARNING: Please ensure you know what you're doing and have read the related issue #1760.
Under no situations should this API be executed as an automated maintenance task!
If a room has lots of forward extremities, the extra can be deleted as follows:
DELETE /_synapse/admin/v1/rooms/<room_id_or_alias>/forward_extremities
A response as follows will be returned, indicating the amount of forward extremities that were
deleted.
{
"deleted": 1
}
GET /_synapse/admin/v1/rooms/<room_id>/context/<event_id>
Example response:
{
"end": "t29-57_2_0_2",
"events_after": [
{
"content": {
"body": "This is an example text message",
"msgtype": "[Link]",
"format": "[Link]",
"formatted_body": "<b>This is an example text message</b>"
},
"type": "[Link]",
"event_id": "$143273582443PhrSn:[Link]",
"room_id": "!636q39766251:[Link]",
"sender": "@example:[Link]",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
}
}
],
"event": {
"content": {
"body": "[Link]",
"info": {
"h": 398,
"w": 394,
"mimetype": "image/jpeg",
"size": 31037
},
"url": "mxc://[Link]/JWEIFJgwEIhweiWJE",
"msgtype": "[Link]"
},
"type": "[Link]",
"event_id": "$f3h4d129462ha:[Link]",
"room_id": "!636q39766251:[Link]",
"sender": "@example:[Link]",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
}
},
"events_before": [
{
"content": {
"body": "[Link]",
"filename": "[Link]",
"info": {
"mimetype": "application/msword",
"size": 46144
},
"msgtype": "[Link]",
"url": "mxc://[Link]/FHyPlCeYUSFFxlgbQYZmoEoe"
},
"type": "[Link]",
"event_id": "$143273582443PhrSn:[Link]",
"room_id": "!636q39766251:[Link]",
"sender": "@example:[Link]",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
}
}
],
"start": "t27-54_2_0_2",
"state": [
{
"content": {
"creator": "@example:[Link]",
"room_version": "1",
"[Link]": true,
"predecessor": {
"event_id": "$something:[Link]",
"room_id": "!oldroom:[Link]"
}
},
"type": "[Link]",
"event_id": "$143273582443PhrSn:[Link]",
"room_id": "!636q39766251:[Link]",
"sender": "@example:[Link]",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
},
"state_key": ""
},
{
"content": {
"membership": "join",
"avatar_url": "mxc://[Link]/SEsfnsuifSDFSSEF",
"displayname": "Alice Margatroid"
},
"type": "[Link]",
"event_id": "$143273582443PhrSn:[Link]",
"room_id": "!636q39766251:[Link]",
"sender": "@example:[Link]",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
},
"state_key": "@alice:[Link]"
}
]
}
Server Notices
The API to send notices is as follows:
POST /_synapse/admin/v1/send_server_notice
or:
PUT /_synapse/admin/v1/send_server_notice/{txnId}
You will need to authenticate with an access token for an admin user.
When using the PUT form, retransmissions with the same transaction ID will be ignored in the
same way as with PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId} .
{
"user_id": "@target_user:server_name",
"content": {
"msgtype": "[Link]",
"body": "This is my message"
}
}
Once the notice has been sent, the API will return the following response:
{
"event_id": "<event_id>"
}
Note that server notices must be enabled in [Link] before this API can be used. See
server_notices.md for more information.
Deprecated: Shutdown room API
The old Shutdown room API is deprecated and will be removed in a future release. See
the new Delete Room API for more details.
Shuts down a room, preventing new joins and moves local users and room aliases
automatically to a new room. The new room will be created with the user specified by the
new_room_user_id parameter as room administrator and will contain a message explaining
what happened. Users invited to the new room will have power level -10 by default, and thus
be unable to speak. The old room's power levels will be changed to disallow any further invites
or joins.
The local server will only have the power to move local user and room aliases to the new room.
Users on other servers will be unaffected.
API
You will need to authenticate with an access token for an admin user.
URL
POST /_synapse/admin/v1/shutdown_room/{room_id}
URL Parameters
new_room_user_id - Required. A string representing the user ID of the user that will
admin the new room that all users in the old room will be moved to.
room_name - Optional. A string representing the name of the room that new users will be
invited to.
message - Optional. A string containing the first message that will be sent as
new_room_user_id in the new room. Ideally this will clearly convey why the original room
was shut down.
If not specified, the default value of room_name is "Content Violation Notification". The default
value of message is "Sharing illegal content on othis server is not permitted and rooms in
violation will be blocked."
Response Parameters
kicked_users - An integer number representing the number of users that were kicked.
failed_to_kick_users - An integer number representing the number of users that were
not kicked.
local_aliases - An array of strings representing the local aliases that were migrated
from the old room to the new.
new_room_id - A string representing the room ID of the new room.
Example
Request:
POST /_synapse/admin/v1/shutdown_room/!somebadroom%[Link]
{
"new_room_user_id": "@someuser:[Link]",
"room_name": "Content Violation Notification",
"message": "Bad Room has been shutdown due to content violations on this
server. Please review our Terms of Service."
}
Response:
{
"kicked_users": 5,
"failed_to_kick_users": 0,
"local_aliases": ["#badroom:[Link]", "#evilsaloon:[Link]],
"new_room_id": "!newroomid:[Link]",
},
With all that being said, if you still want to try and recover the room:
Aliases that would have been redirected to the Content Violation room.
Users that would have been booted from the room (and will have been force-joined to the
Content Violation room).
Removal of the Content Violation room if desired.
Users' media usage statistics
Returns information about all local media usage of users. Gives the possibility to filter them by
time and user.
GET /_synapse/admin/v1/statistics/users/media
To use it, you will need to authenticate by providing an access_token for a server admin: see
Admin API.
{
"users": [
{
"displayname": "foo_user_0",
"media_count": 2,
"media_length": 134,
"user_id": "@foo_user_0:test"
},
{
"displayname": "foo_user_1",
"media_count": 2,
"media_length": 134,
"user_id": "@foo_user_1:test"
}
],
"next_token": 3,
"total": 10
}
To paginate, check for next_token and if present, call the endpoint again with from set to the
value of next_token . This will return a new page.
If the endpoint does not return a next_token then there are no more reports to paginate
through.
Parameters
limit : string representing a positive integer - Is optional but is used for pagination,
denoting the maximum number of items to return in this call. Defaults to 100 .
from : string representing a positive integer - Is optional but used for pagination, denoting
the offset in the returned results. This should be treated as an opaque value and not
explicitly set to anything other than the return value of next_token from a previous call.
Defaults to 0 .
order_by - string - The method in which to sort the returned list of users. Valid values
are:
user_id - Users are ordered alphabetically by user_id . This is the default.
displayname - Users are ordered alphabetically by displayname .
media_length - Users are ordered by the total size of uploaded media in bytes.
Smallest to largest.
media_count - Users are ordered by number of uploaded media. Smallest to largest.
from_ts - string representing a positive integer - Considers only files created at this
timestamp or later. Unix timestamp in ms.
until_ts - string representing a positive integer - Considers only files created at this
timestamp or earlier. Unix timestamp in ms.
search_term - string - Filter users by their user ID localpart or displayname. The search
term can be found in any part of the string. Defaults to no filtering.
dir - string - Direction of order. Either f for forwards or b for backwards. Setting this
value to b will reverse the above sort order. Defaults to f .
Response
users - An array of objects, each containing information about the user and their local
media. Objects contain the following fields:
displayname - string - Displayname of this user.
media_count - integer - Number of uploaded media by this user.
media_length - integer - Size of uploaded media in bytes by this user.
user_id - string - Fully-qualified user ID (ex. @user:[Link] ).
next_token - integer - Opaque value used for pagination. See above.
total - integer - Total number of users after filtering.
User Admin API
GET /_synapse/admin/v2/users/<user_id>
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{
"displayname": "User",
"threepids": [
{
"medium": "email",
"address": "<user_mail_1>"
},
{
"medium": "email",
"address": "<user_mail_2>"
}
],
"avatar_url": "<avatar_url>",
"admin": 0,
"deactivated": 0,
"shadow_banned": 0,
"password_hash": "$2b$12$p9B4GkqYdRTPGD",
"creation_ts": 1560432506,
"appservice_id": null,
"consent_server_notice_sent": null,
"consent_version": null
}
URL parameters:
PUT /_synapse/admin/v2/users/<user_id>
{
"password": "user_password",
"displayname": "User",
"threepids": [
{
"medium": "email",
"address": "<user_mail_1>"
},
{
"medium": "email",
"address": "<user_mail_2>"
}
],
"avatar_url": "<avatar_url>",
"admin": false,
"deactivated": false
}
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
URL parameters:
Body parameters:
password , optional. If provided, the user's password is updated and all devices are logged
out.
threepids , optional, allows setting the third-party IDs (email, msisdn) belonging to a
user.
If the user already exists then optional parameters default to the current value.
In order to re-activate an account deactivated must be set to false . If users do not login via
single-sign-on, a new password must be provided.
List Accounts
This API returns all local user accounts. By default, the response is ordered by ascending user
ID.
GET /_synapse/admin/v2/users?from=0&limit=10&guests=false
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{
"users": [
{
"name": "<user_id1>",
"is_guest": 0,
"admin": 0,
"user_type": null,
"deactivated": 0,
"shadow_banned": 0,
"displayname": "<User One>",
"avatar_url": null
}, {
"name": "<user_id2>",
"is_guest": 0,
"admin": 1,
"user_type": null,
"deactivated": 0,
"shadow_banned": 0,
"displayname": "<User Two>",
"avatar_url": "<avatar_url>"
}
],
"next_token": "100",
"total": 200
}
To paginate, check for next_token and if present, call the endpoint again with from set to the
value of next_token . This will return a new page.
If the endpoint does not return a next_token then there are no more users to paginate
through.
Parameters
user_id - Is optional and filters to only return users with user IDs that contain this value.
This parameter is ignored when using the name parameter.
name - Is optional and filters to only return users with user ID localparts or displaynames
that contain this value.
guests - string representing a bool - Is optional and if false will exclude guest users.
Defaults to true to include guest users.
limit - string representing a positive integer - Is optional but is used for pagination,
denoting the maximum number of items to return in this call. Defaults to 100 .
from - string representing a positive integer - Is optional but used for pagination,
denoting the offset in the returned results. This should be treated as an opaque value and
not explicitly set to anything other than the return value of next_token from a previous
call. Defaults to 0 .
order_by - The method by which to sort the returned list of users. If the ordered field has
duplicates, the second order is always by ascending name , which guarantees a stable
ordering. Valid values are:
Caution. The database only has indexes on the columns name and created_ts . This means
that if a different sort order is used ( is_guest , admin , user_type , deactivated ,
shadow_banned , avatar_url or displayname ), this can cause a large load on the database,
especially for large environments.
Response
users - An array of objects, each containing information about an user. User objects
contain the following fields:
next_token : string representing a positive integer - Indication for pagination. See above.
GET /_synapse/admin/v1/whois/<user_id>
and:
GET /_matrix/client/r0/admin/whois/<userId>
{
"user_id": "<user_id>",
"devices": {
"": {
"sessions": [
{
"connections": [
{
"ip": "[Link]",
"last_seen": 1417222374433,
"user_agent": "Mozilla/5.0 ..."
},
{
"ip": "[Link]",
"last_seen": 1417222374500,
"user_agent": "Dalvik/2.1.0 ..."
}
]
}
]
}
}
}
Deactivate Account
This API deactivates an account. It removes active access tokens, resets the password, and
deletes third-party IDs (to prevent the user requesting a password reset).
It can also mark the user as GDPR-erased. This means messages sent by the user will still be
visible by anyone that was in the room when these messages were sent, but hidden from users
joining the room afterwards.
POST /_synapse/admin/v1/deactivate/<user_id>
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
The erase parameter is optional and defaults to false . An empty body may be passed for
backwards compatibility.
The following additional actions are performed during deactivation if erase is set to true :
Reset password
Changes the password of another user. This will automatically log the user out of all their
devices.
POST /_synapse/admin/v1/reset_password/<user_id>
{
"new_password": "<secret>",
"logout_devices": true
}
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
GET /_synapse/admin/v1/users/<user_id>/admin
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{
"admin": true
}
PUT /_synapse/admin/v1/users/<user_id>/admin
{
"admin": true
}
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
List room memberships of a user
Gets a list of all room_id that a specific user_id is member.
GET /_synapse/admin/v1/users/<user_id>/joined_rooms
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{
"joined_rooms": [
"!DuGcnbhHGaSZQoNQR:[Link]",
"!ZtSaPCawyWtxfWiIy:[Link]"
],
"total": 2
}
The server returns the list of rooms of which the user and the server are member. If the user is
local, all the rooms of which the user is member are returned.
Parameters
Response
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{
"media": [
{
"created_ts": 100400,
"last_access_ts": null,
"media_id": "qXhyRzulkwLsNHTbpHreuEgo",
"media_length": 67,
"media_type": "image/png",
"quarantined_by": null,
"safe_from_quarantine": false,
"upload_name": "[Link]"
},
{
"created_ts": 200400,
"last_access_ts": null,
"media_id": "FHfiSnzoINDatrXHQIXBtahw",
"media_length": 67,
"media_type": "image/png",
"quarantined_by": null,
"safe_from_quarantine": false,
"upload_name": "[Link]"
}
],
"next_token": 3,
"total": 2
}
To paginate, check for next_token and if present, call the endpoint again with from set to the
value of next_token . This will return a new page.
If the endpoint does not return a next_token then there are no more reports to paginate
through.
Parameters
limit : string representing a positive integer - Is optional but is used for pagination,
denoting the maximum number of items to return in this call. Defaults to 100 .
from : string representing a positive integer - Is optional but used for pagination, denoting
the offset in the returned results. This should be treated as an opaque value and not
explicitly set to anything other than the return value of next_token from a previous call.
Defaults to 0 .
order_by - The method by which to sort the returned list of media. If the ordered field
has duplicates, the second order is always by ascending media_id , which guarantees a
stable ordering. Valid values are:
dir - Direction of media order. Either f for forwards or b for backwards. Setting this
value to b will reverse the above sort order. Defaults to f .
If neither order_by nor dir is set, the default order is newest media on top (corresponds to
order_by = created_ts and dir = b ).
Caution. The database only has indexes on the columns media_id , user_id and created_ts .
This means that if a different sort order is used ( upload_name , last_access_ts ,
media_length , media_type , quarantined_by or safe_from_quarantine ), this can cause a
large load on the database, especially for large environments.
Response
media - An array of objects, each containing information about a media. Media objects
contain the following fields:
quarantined_by - string - The user ID that initiated the quarantine request for this
media.
Login as a user
Get an access token that can be used to authenticate as that user. Useful for when admins wish
to do actions on behalf of a user.
POST /_synapse/admin/v1/users/<user_id>/login
{}
An optional valid_until_ms field can be specified in the request body as an integer timestamp
that specifies when the token should expire. By default tokens do not expire.
{
"access_token": "<opaque_access_token_string>"
}
This API does not generate a new device for the user, and so will not appear their /devices list,
and in general the target user should not be able to tell they have been logged in as.
To expire the token call the standard /logout API with the token.
Note: The token will expire if the admin user calls /logout/all from any of their devices, but
the token will not expire if the target user does the same.
User devices
GET /_synapse/admin/v2/users/<user_id>/devices
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{
"devices": [
{
"device_id": "QBUAZIFURK",
"display_name": "android",
"last_seen_ip": "[Link]",
"last_seen_ts": 1474491775024,
"user_id": "<user_id>"
},
{
"device_id": "AUIECTSRND",
"display_name": "ios",
"last_seen_ip": "[Link]",
"last_seen_ts": 1474491775025,
"user_id": "<user_id>"
}
],
"total": 2
}
Parameters
Response
The following fields are returned in the JSON response body:
devices - An array of objects, each containing information about a device. Device objects
contain the following fields:
Deletes the given devices for a specific user_id , and invalidates any access token associated
with them.
POST /_synapse/admin/v2/users/<user_id>/delete_devices
{
"devices": [
"QBUAZIFURK",
"AUIECTSRND"
],
}
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
Parameters
Show a device
GET /_synapse/admin/v2/users/<user_id>/devices/<device_id>
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{
"device_id": "<device_id>",
"display_name": "android",
"last_seen_ip": "[Link]",
"last_seen_ts": 1474491775024,
"user_id": "<user_id>"
}
Parameters
Response
PUT /_synapse/admin/v2/users/<user_id>/devices/<device_id>
{
"display_name": "My other phone"
}
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
Parameters
display_name - The new display name for this device. If not given, the display name is
unchanged.
Delete a device
Deletes the given device_id for a specific user_id , and invalidates any access token
associated with it.
DELETE /_synapse/admin/v2/users/<user_id>/devices/<device_id>
{}
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
Parameters
The following parameters should be set in the URL:
GET /_synapse/admin/v1/users/<user_id>/pushers
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{
"pushers": [
{
"app_display_name":"HTTP Push Notifications",
"app_id":"[Link]",
"data": {
"url":"[Link]"
},
"device_display_name":"pushy push",
"kind":"http",
"lang":"None",
"profile_tag":"",
"pushkey":"a@[Link]"
}
],
"total": 1
}
Parameters
Response
app_display_name - string - A string that will allow the user to identify what
application owns this pusher.
app_id - string - This is a reverse-DNS style identifier for the application. Max length,
64 chars.
url - string - Required if kind is http . The URL to use to send notifications
to.
format - string - The format to use when sending notifications to the Push
Gateway.
device_display_name - string - A string that will allow the user to identify what
device owns this pusher.
profile_tag - string - This string determines which set of device specific rules this
pusher executes.
kind - string - The kind of pusher. "http" is a pusher that sends HTTP pokes.
lang - string - The preferred language for receiving notifications (e.g. 'en' or 'en-US')
profile_tag - string - This string determines which set of device specific rules this
pusher executes.
pushkey - string - This is a unique identifier for this pusher. Max length, 512 bytes.
Shadow-banning users
Shadow-banning is a useful tool for moderating malicious or egregiously abusive users. A
shadow-banned users receives successful responses to their client-server API requests, but the
events are not propagated into rooms. This can be an effective tool as it (hopefully) takes
longer for the user to realise they are being moderated before pivoting to another account.
Shadow-banning a user should be used as a tool of last resort and may lead to confusing or
broken behaviour for the client. A shadow-banned user will not receive any notification and it is
generally more appropriate to ban or kick abusive users. A shadow-banned user will be unable
to contact anyone on the server.
POST /_synapse/admin/v1/users/<user_id>/shadow_ban
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
Parameters
user_id - The fully qualified MXID: for example, @user:[Link] . The user must be
local.
GET /_synapse/admin/v1/users/<user_id>/override_ratelimit
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{
"messages_per_second": 0,
"burst_count": 0
}
Parameters
The following parameters should be set in the URL:
user_id - The fully qualified MXID: for example, @user:[Link] . The user must be
local.
Response
{}
Set ratelimit
POST /_synapse/admin/v1/users/<user_id>/override_ratelimit
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{
"messages_per_second": 0,
"burst_count": 0
}
Parameters
user_id - The fully qualified MXID: for example, @user:[Link] . The user must be
local.
Body parameters:
Response
Delete ratelimit
DELETE /_synapse/admin/v1/users/<user_id>/override_ratelimit
To use it, you will need to authenticate by providing an access_token for a server admin:
Admin API
{}
Parameters
user_id - The fully qualified MXID: for example, @user:[Link] . The user must be
local.
Version API
This API returns the running Synapse version and the Python version on which Synapse is being
run. This is useful when a Synapse instance is behind a proxy that does not forward the 'Server'
header (which also contains Synapse version information).
GET /_synapse/admin/v1/server_version
{
"server_version": "0.99.2rc1 (b=develop, abcdef123)",
"python_version": "3.6.8"
}
Using the synapse manhole
The "manhole" allows server administrators to access a Python shell on a running Synapse
installation. This is a very powerful mechanism for administration and debugging.
Security Warning
Note that this will give administrative access to synapse to all users with shell access to the
server. It should therefore not be enabled in environments where untrusted users have shell
access.
To enable it, first uncomment the manhole listener configuration in [Link] . The
configuration is slightly different if you're using docker.
Docker config
listeners:
- port: 9000
bind_addresses: ['[Link]']
type: manhole
When using docker run to start the server, you will then need to change the command to the
following to include the manhole port forwarding. The -p [Link]:9000:9000 below is
important: it ensures that access to the manhole is only possible for local users.
Native config
If you are not using docker, set bind_addresses to ['::1', '[Link]'] as shown. The
bind_addresses in the example below is important: it ensures that access to the manhole is
only possible for local users).
listeners:
- port: 9000
bind_addresses: ['::1', '[Link]']
type: manhole
Then restart synapse, and point an ssh client at port 9000 on localhost, using the username
matrix :
This gives a Python REPL in which hs gives access to the [Link] object -
which in turn gives access to many other parts of the process.
Note that any call which returns a coroutine will need to be wrapped in ensureDeferred .
The first serves the metrics as a part of the usual web server and can be enabled by
adding the "metrics" resource to the existing listener as such:
resources:
- names:
- client
- metrics
This provides a simple way of adding metrics to your Synapse installation, and serves
under /_synapse/metrics . If you do not wish your metrics be publicly exposed, you will
need to either filter it out at your load balancer, or use the second method.
The second method runs the metrics server on a different port, in a different thread to
Synapse. This can make it more resilient to heavy load meaning metrics cannot be
retrieved, and can be exposed to just internal networks easier. The served metrics are
available over HTTP only, and will be available at /_synapse/metrics .
listeners:
- type: metrics
port: 9000
bind_addresses:
- '[Link]'
For both options, you will need to ensure that enable_metrics is set to True .
3. Restart Synapse.
- job_name: "synapse"
scrape_interval: 15s
metrics_path: "/_synapse/metrics"
static_configs:
- targets: ["[Link]:port"]
where [Link] is the IP address of Synapse, and port is the listener port
configured with the metrics resource.
If your prometheus is older than 1.5.2, you will need to replace static_configs in the
above with target_groups .
5. Restart Prometheus.
Monitoring workers
To monitor a Synapse installation using workers, every worker needs to be monitored
independently, in addition to the main homeserver process. This is because workers don't send
their metrics to the main homeserver process, but expose them directly (if they are configured
to do so).
To allow collecting metrics from a worker, you need to add a metrics listener to its
configuration, by adding the following under worker_listeners :
- type: metrics
bind_address: ''
port: 9101
The bind_address and port parameters should be set so that the resulting listener can be
reached by prometheus, and they don't clash with an existing worker. With this example, the
worker's metrics would then be available on [Link] .
Labels ( instance , job , index ) can be defined as anything. The labels are used to group
graphs in grafana.
New Name
python_gc_objects_collected_total python_
python_gc_objects_uncollectable_total python_
python_gc_collections_total python_
process_cpu_seconds_total process
synapse_federation_client_sent_transactions_total synapse
synapse_federation_client_events_processed_total synapse
synapse_event_processing_loop_count_total synapse
synapse_event_processing_loop_room_count_total synapse
New Name
synapse_util_metrics_block_count_total synapse
synapse_util_metrics_block_time_seconds_total synapse
synapse_util_metrics_block_ru_utime_seconds_total synapse
synapse_util_metrics_block_ru_stime_seconds_total synapse
synapse_util_metrics_block_db_txn_count_total synapse
synapse_util_metrics_block_db_txn_duration_seconds_total synapse
synapse_util_metrics_block_db_sched_duration_seconds_total synapse
synapse_background_process_start_count_total synapse
synapse_background_process_ru_utime_seconds_total synapse
synapse_background_process_ru_stime_seconds_total synapse
synapse_background_process_db_txn_count_total synapse
synapse_background_process_db_txn_duration_seconds_total synapse
synapse_background_process_db_sched_duration_seconds_total synapse
synapse_storage_events_persisted_events_total synapse
synapse_storage_events_persisted_events_sep_total synapse
synapse_storage_events_state_delta_total synapse
synapse_storage_events_state_delta_single_event_total synapse
synapse_storage_events_state_delta_reuse_delta_total synapse
synapse_federation_server_received_pdus_total synapse
synapse_federation_server_received_edus_total synapse
synapse_handler_presence_notified_presence_total synapse
synapse_handler_presence_federation_presence_out_total synapse
synapse_handler_presence_presence_updates_total synapse
synapse_handler_presence_timers_fired_total synapse
synapse_handler_presence_federation_presence_total synapse
synapse_handler_presence_bump_active_time_total synapse
synapse_federation_client_sent_edus_total synapse
synapse_federation_client_sent_pdu_destinations_count_total synapse
synapse_federation_client_sent_pdu_destinations_total synapse
synapse_handlers_appservice_events_processed_total synapse
synapse_notifier_notified_events_total synapse
synapse_push_bulk_push_rule_evaluator_push_rules_invalidation_counter_total synapse
New Name
synapse_push_bulk_push_rule_evaluator_push_rules_state_size_counter_total synapse
synapse_http_httppusher_http_pushes_processed_total synapse
synapse_http_httppusher_http_pushes_failed_total synapse
synapse_http_httppusher_badge_updates_processed_total synapse
synapse_http_httppusher_badge_updates_failed_total synapse
All time duration-based metrics have been changed to be seconds. This affects:
Several metrics have been changed to be histograms, which sort entries into buckets and allow
better analysis. The following metrics are now histograms:
Altered metrics
python_gc_time
python_twisted_reactor_pending_calls
python_twisted_reactor_tick_time
synapse_http_server_response_time_seconds
synapse_storage_query_time
synapse_storage_schedule_time
synapse_storage_transaction_time
Block and response metrics renamed for 0.27.0
Synapse 0.27.0 begins the process of rationalising the duplicate *:count metrics reported for
the resource tracking for code blocks and HTTP requests.
At the same time, the corresponding *:total metrics are being renamed, as the :total suffix
no longer makes sense in the absence of a corresponding :count metric.
To enable a graceful migration path, this release just adds new names for the metrics being
renamed. A future release will remove the old ones.
The following table shows the new metrics, and the old metrics which they are replacing.
See the following for how to decode the dense data available from the default logging
configuration.
Part Explanation
AAAA Timestamp request was logged (not recieved)
Logger name ( [Link].(http\|https).<tag> , where 'tag' is defined
BBBB
in the listeners config section, normally the port)
CCCC Line number in code
DDDD Log Level
EEEE Request Identifier (This identifier is shared by related log lines)
FFFF Source IP (Or X-Forwarded-For if enabled)
GGGG Server Port
Federated Server or Local User making request (blank if unauthenticated or
HHHH
not supplied)
IIII Total Time to process the request
Time to send response over network once generated (this may be negative if
JJJJ
the socket is closed before the response is generated)
KKKK Userland CPU time
LLLL System CPU time
Total time waiting for a free DB connection from the pool across all parallel
MMMM
DB work from this request
Total time waiting for response to DB queries across all parallel DB work
NNNN
from this request
OOOO Count of DB transactions performed
PPPP Response body size
Part Explanation
Response status code (prefixed with ! if the socket was closed before the
QQQQ
response was generated)
RRRR Request
SSSS User-agent
Events fetched from DB to service this request (note that this does not
TTTT
include events fetched from the cache)
MMMM / NNNN can be greater than IIII if there are multiple slow database queries running in
parallel.
Some actions can result in multiple identical http requests, which will return the same data, but
only the first request will report time/transactions in KKKK / LLLL / MMMM / NNNN / OOOO - the
others will be awaiting the first query to return a response and will simultaneously return with
the first request, but with very small processing times.
Contributing
Welcome to Synapse
This document aims to get you started with contributing to this repo!
The source code of Synapse is hosted on GitHub. You will also need a recent version of git.
If you need help getting started with git, this is beyond the scope of the document, but you can
find many good git tutorials on the web.
cd path/where/you/have/cloned/the/repository
python3 -m venv ./env
source ./env/bin/activate
pip install -e ".[all,lint,mypy,test]"
pip install tox
5. Get in touch.
Join our developer community on Matrix: #synapse-dev:[Link] !
6. Pick an issue.
Fix your favorite problem or perhaps find a Good First Issue to work on.
There is a growing amount of documentation located in the docs directory. This documentation
is intended primarily for sysadmins running their own Synapse instance, as well as developers
interacting externally with Synapse. docs/dev exists primarily to house documentation for
Synapse developers. docs/admin_api houses documentation regarding Synapse's Admin API,
which is used mostly by sysadmins and external service developers.
If you add new files added to either of these folders, please use GitHub-Flavoured Markdown.
Some documentation also exists in Synapse's GitHub Wiki, although this is primarily
contributed to by community authors.
source ./env/bin/activate
./scripts-dev/[Link]
Note that this script will modify your files to fix styling errors. Make sure that you have saved all
your files.
If you wish to restrict the linters to only the files changed since the last commit (much faster!),
you can instead run:
source ./env/bin/activate
./scripts-dev/[Link] -d
Or if you know exactly which files you wish to lint, you can instead run:
source ./env/bin/activate
./scripts-dev/[Link] path/to/[Link] path/to/[Link] path/to/folder
source ./env/bin/activate
trial tests
If you wish to only run some unit tests, you may specify another module instead of tests - or a
test class or a method:
source ./env/bin/activate
trial [Link].test_room
[Link].test_admin.ExfiltrateData.test_invite
If your tests fail, you may wish to look at the logs (the default log level is ERROR ):
less _trial_temp/[Link]
The following command will let you run the integration test with the most common
configuration:
This configuration should generally cover your needs. For more details about other
configurations, see documentation in the SyTest repo.
1. verify that all the tests pass, including the coding style;
2. sign off your contribution;
3. git push your commit to your fork of Synapse;
4. on GitHub, create the Pull Request;
5. add a changelog entry and push it to your Pull Request;
6. for most contributors, that's all - however, if you are a member of the organization
matrix-org , on GitHub, please request a review from [Link] / Synapse Core .
Changelog
All changes, even minor ones, need a corresponding changelog / newsfragment entry. These
are managed by Towncrier.
To create a changelog entry, make a new file in the changelog.d directory named in the
format of [Link] . The type can be one of the following:
feature
bugfix
docker (for updates to the Docker image)
doc (for updates to the documentation)
removal (also used for deprecations)
misc (for internal-only changes)
This file will become part of our changelog at the next release, so the content of the file should
be a short description of your change in the same style as the rest of the changelog. The file
can contain Markdown formatting, and should end with a full stop (.) or an exclamation mark (!)
for consistency.
Adding credits to the changelog is encouraged, we value your contributions and would like to
have you shouted out in the release notes!
For example, a fix in PR #1234 would have its changelog entry in changelog.d/[Link] ,
and contain content like:
The security levels of Florbs are now validated when received via the /federation/florb
endpoint. Contributed by Jane Matrix.
If there are multiple pull requests involved in a single bugfix/feature/etc, then the content for
each changelog.d file should be the same. Towncrier will merge the matching files together
into a single changelog entry when we come to release.
How do I know what to call the changelog file before I create the PR?
Obviously, you don't know if you should call your newsfile [Link] or [Link] until
you create the PR, which leads to a chicken-and-egg problem.
1. Open the PR without a changelog file, see what number you got, and then add the
changelog file to your branch (see Updating your pull request), or:
2. Look at the list of all issues/PRs, add one to the highest number you see, and quickly open
the PR before somebody else claims your number.
This script might be helpful if you find yourself doing this a lot.
Sorry, we know it's a bit fiddly, but it's really helpful for us when we come to put together a
release!
Debian changelog
Changes which affect the debian packaging files (in debian ) are an exception to the rule that
all changes require a changelog.d file.
In this case, you will need to add an entry to the debian changelog for the next release. For this,
run the following command:
dch
This will make up a new version number (if there isn't already an unreleased version in flight),
and open an editor where you can add a new changelog entry. (Our release process will ensure
that the version number and maintainer name is corrected for the release.)
If your change affects both the debian packaging and files outside the debian directory, you will
need both a regular newsfragment and an entry in the debian changelog. (Though typically
such changes should be submitted as two separate pull requests.)
Sign off
In order to have a concrete record that your contribution is intentional and you agree to license
it under the same terms as the project's license, we've adopted the same lightweight approach
that the Linux Kernel submitting patches process, Docker, and many other projects use: the
DCO (Developer Certificate of Origin: [Link] This is a simple
declaration that you wrote the contribution or otherwise have the right to contribute it to
Matrix:
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
If you agree to this for your contribution, then all that's needed is to include the line in your
commit or pull request comment:
We accept contributions under a legally identifiable name, such as your name on government
documentation or common-law names (names claimed by legitimate usage or repute).
Unfortunately, we cannot accept anonymous contributions at this time.
Git allows you to add this signoff automatically when using the -s flag to git commit , which
uses the name and email set in your [Link] and [Link] git configs.
10. Turn feedback into better code.
Once the Pull Request is opened, you will see a few things:
1. our automated CI (Continuous Integration) pipeline will run (again) the linters, the unit
tests, the integration tests and more;
2. one or more of the developers will take a look at your Pull Request and offer feedback.
Once both the CI and the developers are happy, the patch will be merged into Synapse and
released shortly!
Conclusion
That's it! Matrix is a very open and collaborative project as you might expect given our
obsession with open communication. If we're going to successfully matrix together all the
fragmented communication technologies out there we are reliant on contributions and
collaboration from the community to do so. So please get involved - and we hope you have as
much fun hacking on Matrix as we do!
Code Style
Formatting tools
The Synapse codebase uses a number of code formatting tools in order to quickly and
automatically check for formatting (and sometimes logical) errors in code.
black
The Synapse codebase uses black as an opinionated code formatter, ensuring all comitted
code is properly formatted.
Have black auto-format your code (it shouldn't change any functionality) with:
black . --exclude="\.tox|build|env"
flake8
flake8 is a code checking tool. We require code to pass flake8 before being merged
into the codebase.
isort
isort ensures imports are nicely formatted, and can suggest and auto-fix issues such as
double-importing.
General rules
Naming:
Use camel case for class and type names
Use underscores for functions and variables.
Docstrings: should follow the google code style. See the examples in the sphinx
documentation.
Imports:
Example:
is preferred over:
This goes against the advice in the Google style guide, but it means that errors in the
name are caught early (at import time).
Avoid wildcard imports ( from [Link] import * ) and relative imports ( from
.types import UserID ).
Configuration file format
The sample configuration file acts as a reference to Synapse's configuration options for server
administrators. Remember that many readers will be unfamiliar with YAML and server
administration in general, so that it is important that the file be as easy to understand as
possible, which includes following a consistent format.
Sections should be separated with a heading consisting of a single line prefixed and
suffixed with ## . There should be two blank lines before the section header, and one
after.
Each option should be listed in the file with the following format:
A comment describing the setting. Each line of this comment should be prefixed
with a hash ( # ) and a space.
The comment should describe the default behaviour (ie, what happens if the setting
is omitted), as well as what the effect will be if the setting is changed.
Often, the comment end with something like "uncomment the following to ".
For boolean (on/off) options, convention is that this example should be the opposite
to the default (so the comment will end with "Uncomment the following to enable
[or disable] ." For other options, the example should give some non-default value
which is likely to be useful to the reader.
Where several settings are grouped into a single dict, avoid the convention where the
whole block is commented out, resulting in comment lines starting # # , as this is hard to
read and confusing to edit. Instead, leave the top-level config option uncommented, and
follow the conventions above for sub-options. Ensure that your code correctly handles the
top-level option being set to None (as it will be if no sub-options are enabled).
true and false are spelt thus (as opposed to True , etc.)
Use single quotes ( ' ) rather than double-quotes ( " ) or backticks ( ` ) to refer to
configuration options.
Example:
## Frobnication ##
# The frobnicator will ensure that all requests are fully frobnicated.
# To enable it, uncomment the following.
#
#frobnicator_enabled: true
Note that the sample configuration is generated from the synapse code and is maintained by a
script, scripts-dev/generate_sample_config . Making sure that the output from this script
matches the desired format is left as an exercise for the reader!
Some notes on how we use git
Note how the commit comment explains clearly what is changing and why. Also note the
absence of merge commits, as well as the absence of commits called things like (to pick a few
culprits): “pep8”, “fix broken test”, “oops”, “typo”, or “Who's the president?”.
There are a number of reasons why keeping a clean commit history is a good thing:
From time to time, after a change lands, it turns out to be necessary to revert it, or to
backport it to a release branch. Those operations are much easier when the change is
contained in a single commit.
Similarly, it's much easier to answer questions like “is the fix for /publicRooms on the
release branch?” if that change consists of a single commit.
Likewise: “what has changed on this branch in the last week?” is much clearer without
merges and “pep8” commits everywhere.
Sometimes we need to figure out where a bug got introduced, or some behaviour
changed. One way of doing that is with git bisect : pick an arbitrary commit between
the known good point and the known bad point, and see how the code behaves.
However, that strategy fails if the commit you chose is the middle of someone's epic
branch in which they broke the world before putting it back together again.
One counterargument is that it is sometimes useful to see how a PR evolved as it went through
review cycles. This is true, but that information is always available via the GitHub UI (or via the
little-known refs/pull namespace).
Of course, in reality, things are more complicated than that. We have release branches as well
as develop and master , and we deliberately merge changes between them. Bugs often slip
through and have to be fixed later. That's all fine: this not a cast-iron rule which must be
obeyed, but an ideal to aim towards.
The TL;DR is: when you come to merge a pull request, you probably want to “squash and
merge”:
.
(This applies whether you are merging your own PR, or that of another contributor.)
“Squash and merge”1 takes all of the changes in the PR, and bundles them into a single commit.
GitHub gives you the opportunity to edit the commit message before you confirm, and
normally you should do so, because the default will be useless (again: * woops typo is not a
useful thing to keep in the historical record).
The main problem with this approach comes when you have a series of pull requests which
build on top of one another: as soon as you squash-merge the first PR, you'll end up with a
stack of conflicts to resolve in all of the others. In general, it's best to avoid this situation in the
first place by trying not to have multiple related PRs in flight at the same time. Still, sometimes
that's not possible and doing a regular merge is the lesser evil.
Another occasion in which a regular merge makes more sense is a PR where you've deliberately
created a series of commits each of which makes sense in its own right. For example: a PR
which gradually propagates a refactoring operation through the codebase, or a PR which is the
culmination of several other PRs. In this case the ability to figure out when a particular
change/bug was introduced could be very useful.
Ultimately: this is not a hard-and-fast-rule. If in doubt, ask yourself “do each of the commits I
am about to merge make sense in their own right”, but remember that we're just doing our
best to balance “keeping the commit history clean” with other factors.
Meanwhile, merging (or squashing, as per the above) from a less-stable to a more-stable
branch is a deliberate action in which you want to publish a change or a set of changes to
(some subset of) the world: for example, this happens when a PR is landed, or as part of our
release process.
So, what counts as a more- or less-stable branch? A little reflection will show that our active
branches are ordered thus, from more-stable to less-stable:
The corollary is: if you have a bugfix that needs to land in both release-vX.Y and develop ,
then you should base your PR on release-vX.Y , get it merged there, and then merge from
release-vX.Y to develop . (If a fix lands in develop and we later need it in a release-branch,
we can of course cherry-pick it, but landing it in the release branch first helps reduce the
chance of annoying conflicts.)
[1]: “Squash and merge” is GitHub's term for this operation. Given that there is no merge
involved, I'm not convinced it's the most intuitive name. ^
[3]: Very, very occasionally (I think this has happened once in the history of Synapse), we've had
two releases in flight at once. Obviously, release-v1.2 is more-stable than release-v1.3 . ^
OpenTracing
Background
OpenTracing is a semi-standard being adopted by a number of distributed tracing platforms. It
is a common api for facilitating vendor-agnostic tracing instrumentation. That is, we can use the
OpenTracing api and select one of a number of tracer implementations to do the heavy lifting
in the background. Our current selected implementation is Jaeger.
OpenTracing is a tool which gives an insight into the causal relationship of work done in and
between servers. The servers each track events and report them to a centralised server - in
Synapse's case: Jaeger. The basic unit used to represent events is the span. The span roughly
represents a single piece of work that was done and the time at which it occurred. A span can
have child spans, meaning that the work of the child had to be completed for the parent span
to complete, or it can have follow-on spans which represent work that is undertaken as a result
of the parent but is not depended on by the parent to in order to finish.
It is up to the remote server to decide what it does with the spans it creates. This is called the
sampling policy and it can be configured through Jaeger's settings.
Setting up OpenTracing
To receive OpenTracing spans, start up a Jaeger server. This can be done using docker like so:
docker run -d --name jaeger \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 14268:14268 \
jaegertracing/all-in-one:1
opentracing:
enabled: true
homeserver_whitelist:
- "[Link]"
- "*.[Link]"
Homeserver whitelisting
The homeserver whitelist is configured using regular expressions. A list of regular expressions
can be given and their union will be compared when propagating any spans contexts to
another homeserver.
Though it's mostly safe to send and receive span contexts to and from untrusted users since
span contexts are usually opaque ids it can lead to two problems, namely:
If the span context is marked as sampled by the sending homeserver the receiver will
sample it. Therefore two homeservers with wildly different sampling policies could incur
higher sampling counts than intended.
Sending servers can attach arbitrary data to spans, known as 'baggage'. For safety this has
been disabled in Synapse but that doesn't prevent another server sending you baggage
which will be logged to OpenTracing's logs.
Configuring Jaeger
Sampling strategies can be set as in this document:
[Link]
Synapse database schema files
Synapse's database schema is stored in the [Link] module.
Logical databases
Synapse supports splitting its datastore across multiple physical databases (which can be
useful for large installations), and the schema files are therefore split according to the logical
database they apply to.
state - used to store Matrix room state (more specifically, state_groups , their
relationships and contents).
main - stores everything else.
Additionally, the common directory contains schema files for tables which must be present on
all physical databases.
Generally, the goal is to maintain compatibility with at least one or two previous releases of
Synapse, so any substantial change tends to require multiple releases and a bit of forward-
planning to get right.
2. Next (say in Synapse v1.37.0): remove the code that writes to room_stats_historical , but
don’t yet remove the table in case of rollback to v1.36.0. Again, we increase
[Link].SCHEMA_VERSION , but because we have not broken compatibility
with v1.36, we do not yet update SCHEMA_COMPAT_VERSION . We now have:
3. Later (say in Synapse v1.38.0): we can remove the table altogether. This will break
compatibility with v1.36.0, so we must update SCHEMA_COMPAT_VERSION accordingly. There
is no need to update [Link].SCHEMA_VERSION , since there is no change
to the Synapse codebase here. So we end up with:
If in doubt about whether to update SCHEMA_VERSION or not, it is generally best to lean towards
doing so.
If you want to recreate these schemas, they need to be made from a database that has had all
background updates run.
NB at the time of writing, this script predates the split into separate state / main databases so
will require updates to handle that correctly.
Boolean columns
Boolean columns require special treatment, since SQLite treats booleans the same as integers.
Before SQLite 3.23, TRUE and FALSE were not recognised as constants by SQLite, and the
IS [NOT] TRUE / IS [NOT] FALSE operators were not supported. This makes it necessary
to avoid using TRUE and FALSE constants in SQL commands.
Default values for new boolean columns present a particular difficulty. Generally it is best
to create separate schema files for Postgres and SQLite. For example:
# in [Link]:
ALTER TABLE tbl ADD COLUMN col BOOLEAN DEFAULT FALSE;
# in [Link]:
ALTER TABLE tbl ADD COLUMN col BOOLEAN DEFAULT 0;
Note that there is a particularly insidious failure mode here: the Postgres flavour will be
accepted by SQLite 3.22, but will give a column whose default value is the string "FALSE"
- which, when cast back to a boolean in Python, evaluates to True .
Log Contexts
To help track the processing of individual requests, synapse uses a ' log context ' to track
which request it is handling at any given moment. This is done via a thread-local variable; a
[Link] is then used to fish the information back out of the thread-local variable and
add it to each log record.
Logcontexts are also used for CPU and database accounting, so that we can track which
requests were responsible for high CPU use or database activity.
The [Link] module provides a facilities for managing the current log
context (as well as providing the LoggingContextFilter class).
Deferreds make the whole thing complicated, so this document describes how it all works, and
how to write code which follows the rules.
In the absence of any Deferred voodoo, things are simple enough. As with any code of this
nature, the rule is that our function should leave things as it found them:
def handle_request(request_id):
request_context = [Link]()
calling_context = context.set_current_context(request_context)
try:
request_context.request = request_id
do_request_handling()
[Link]("finished")
finally:
context.set_current_context(calling_context)
def do_request_handling():
[Link]("phew") # this will be logged against request_id
LoggingContext implements the context management methods, so the above can be written
much more succinctly as:
def handle_request(request_id):
with [Link]() as request_context:
request_context.request = request_id
do_request_handling()
[Link]("finished")
def do_request_handling():
[Link]("phew")
@[Link]
def handle_request(request_id):
with [Link]() as request_context:
request_context.request = request_id
yield do_request_handling()
[Link]("finished")
So we have stopped processing the request (and will probably go on to start processing the
next), without clearing the logcontext.
To circumvent this problem, synapse code assumes that, wherever you have a deferred, you
will want to yield on it. To that end, whereever functions return a deferred, we adopt the
following conventions:
If the deferred is already complete, the function returns with the same logcontext it
started with.
If the deferred is incomplete, the function clears the logcontext before returning;
when the deferred completes, it restores the logcontext before running any
callbacks.
That sounds complicated, but actually it means a lot of code (including the example above)
"just works". There are two cases:
Once do_request_handling 's deferred completes, it will reinstate the logcontext, before
running the callback added by the inlineCallbacks wrapper. That callback runs the
second half of handle_request , so again the "finished" line will be logged against the
right context, and the with block restores the original context.
As an aside, it's worth noting that handle_request follows our rules -though that only matters
if the caller has its own logcontext which it cares about.
The following sections describe pitfalls and helpful patterns when implementing these rules.
d = more_stuff()
result = yield d # also fine, of course
return result
def nonInlineCallbacksFun():
[Link]("just a wrapper really")
return do_some_stuff() # this is ok too - the caller will yield on
# it anyway.
Provided this pattern is followed all the way back up to the callchain to where the logcontext
was set, this will make things work out ok: provided do_some_stuff and more_stuff follow
the rules above, then so will fun (as wrapped by inlineCallbacks ) and
nonInlineCallbacksFun .
It's all too easy to forget to yield : for instance if we forgot that do_some_stuff returned a
deferred, we might plough on regardless. This leads to a mess; it will probably work itself out
eventually, but not before a load of stuff has been logged against the wrong context. (Normally,
other things will break, more obviously, if you forget to yield , so this tends not to be a major
problem in practice.)
Of course sometimes you need to do something a bit fancier with your Deferreds - not all code
follows the linear A-then-B-then-C pattern. Notes on implementing more complex patterns are
in later sections.
That doesn't follow the rules, but we can fix it by wrapping it with PreserveLoggingContext
and yield ing on it:
@[Link]
def sleep(seconds):
with PreserveLoggingContext():
yield get_sleep_deferred(seconds)
This technique works equally for external functions which return deferreds, or deferreds we
have made ourselves.
You can also use context.make_deferred_yieldable , which just does the boilerplate for you,
so the above could be written:
def sleep(seconds):
return context.make_deferred_yieldable(get_sleep_deferred(seconds))
Fire-and-forget
Sometimes you want to fire off a chain of execution, but not wait for its result. That might look
a bit like this:
@[Link]
def do_request_handling():
yield foreground_operation()
# *don't* do this
background_operation()
@[Link]
def background_operation():
yield first_background_step()
[Link]("Completed first step")
yield second_background_step()
[Link]("Completed second step")
The above code does a couple of steps in the background after do_request_handling has
finished. The log lines are still logged against the request_context logcontext, which may or
may not be desirable. There are two big problems with the above, however. The first problem
is that, if background_operation returns an incomplete Deferred, it will expect its caller to
yield immediately, so will have cleared the logcontext. In this example, that means that
'Request handling complete' will be logged without any context.
The second problem, which is potentially even worse, is that when the Deferred returned by
background_operation completes, it will restore the original logcontext. There is nothing
waiting on that Deferred, so the logcontext will leak into the reactor and possibly get attached
to some arbitrary future operation.
@[Link]
def do_request_handling():
yield foreground_operation()
Obviously that option means that the operations done in background_operation would be not
be logged against a logcontext (though that might be fixed by setting a different logcontext via
a with LoggingContext(...) in background_operation ).
context.run_in_background(background_operation)
d1 = operation1()
d2 = operation2()
d3 = [Link]([d1, d2])
This is really a variation of the fire-and-forget problem above, in that we are firing off d1 and
d2 without yielding on them. The difference is that we now have third-party code attached to
their callbacks. Anyway either technique given in the Fire-and-forget section will work.
So, option one: reset the logcontext before starting the operations to be gathered:
@[Link]
def do_request_handling():
with PreserveLoggingContext():
d1 = operation1()
d2 = operation2()
result = yield [Link]([d1, d2])
In this case particularly, though, option two, of using context.preserve_fn almost certainly
makes more sense, so that operation1 and operation2 are both logged against the original
logcontext. This looks like:
@[Link]
def do_request_handling():
d1 = context.preserve_fn(operation1)()
d2 = context.preserve_fn(operation2)()
with PreserveLoggingContext():
result = yield [Link]([d1, d2])
We're not going to rewrite Synapse now, so the following is entirely of academic interest, but I'd
like to record some thoughts on an alternative approach.
I briefly prototyped some code following an alternative set of rules. I think it would work, but I
certainly didn't get as far as thinking how it would interact with concepts as complicated as the
cache descriptors.
functions always preserve the logcontext of their caller, whether or not they are returning
a Deferred.
Deferreds returned by synapse functions run their callbacks in the same context as the
function was orignally called in.
The main point of this scheme is that everywhere that sets the logcontext is responsible for
clearing it before returning control to the reactor.
So, for example, if you were the function which started a with LoggingContext block, you
wouldn't yield within it --- instead you'd start off the background process, and then leave the
with block to wait for it:
def handle_request(request_id):
with [Link]() as request_context:
request_context.request = request_id
d = do_request_handling()
def cb(r):
[Link]("finished")
[Link](cb)
return d
(in general, mixing with LoggingContext blocks and [Link] in the same
function leads to slighly counter-intuitive code, under this scheme).
Because we leave the original with block as soon as the Deferred is returned (as opposed to
waiting for it to be resolved, as we do today), the logcontext is cleared before control passes
back to the reactor; so if there is some code within do_request_handling which needs to wait
for a Deferred to complete, there is no need for it to worry about clearing the logcontext before
doing so:
def handle_request():
r = do_some_stuff()
[Link](do_some_more_stuff)
return r
--- and provided do_some_stuff follows the rules of returning a Deferred which runs its
callbacks in the original logcontext, all is happy.
The business of a Deferred which runs its callbacks in the original logcontext isn't hard to
achieve --- we have it today, in the shape of context._PreservingContextDeferred :
def do_some_stuff():
deferred = do_some_io()
pcd = _PreservingContextDeferred(LoggingContext.current_context())
[Link](pcd)
return pcd
It turns out that, thanks to the way that Deferreds chain together, we automatically get the
property of a context-preserving deferred with [Link] , provided the final
Defered the function yields on has that property. So we can just write:
@[Link]
def handle_request():
yield do_some_stuff()
yield do_some_more_stuff()
To conclude: I think this scheme would have worked equally well, with less danger of messing it
up, and probably made some more esoteric code easier to write. But again --- changing the
conventions of the entire Synapse codebase is not a sensible option for the marginal
improvement offered.
A note on garbage-collection of Deferred chains
It turns out that our logcontext rules do not play nicely with Deferred chains which get
orphaned and garbage-collected.
listener_queue = []
def on_something_interesting():
for d in listener_queue:
[Link]("foo")
@[Link]
def await_something_interesting():
new_deferred = [Link]()
listener_queue.append(new_deferred)
with PreserveLoggingContext():
yield new_deferred
Obviously, the idea here is that we have a bunch of things which are waiting for an event. (It's
just an example of the problem here, but a relatively common one.)
Now let's imagine two further things happen. First of all, whatever was waiting for the
interesting thing goes away. (Perhaps the request times out, or something even more
interesting happens.)
Secondly, let's suppose that we decide that the interesting thing is never going to happen, and
we reset the listener queue:
def reset_listener_queue():
listener_queue.clear()
So, both ends of the deferred chain have now dropped their references, and the deferred chain
is now orphaned, and will be garbage-collected at some point. Note that
await_something_interesting is a generator function, and when Python garbage-collects
generator functions, it gives them a chance to clean up by making the yield raise a
GeneratorExit exception. In our case, that means that the __exit__ handler of
PreserveLoggingContext will carefully restore the request context, but there is now nothing
waiting for its return, so the request context is never cleared.
To reiterate, this problem only arises when both ends of a deferred chain are dropped.
Dropping the the reference to a deferred you're supposed to be calling is probably bad
practice, so this doesn't actually happen too much. Unfortunately, when it does happen, it will
lead to leaked logcontexts which are incredibly hard to track down.
Replication Architecture
Motivation
We'd like to be able to split some of the work that synapse does into multiple python
processes. In theory multiple synapse processes could share a single postgresql database and
we'd scale up by running more synapse processes. However much of synapse assumes that
only one process is interacting with the database, both for assigning unique identifiers when
inserting into tables, notifying components about new updates, and for invalidating its caches.
So running multiple copies of the current code isn't an option. One way to run multiple
processes would be to have a single writer process and multiple reader processes connected to
the same database. In order to do this we'd need a way for the reader process to invalidate its
in-memory caches when an update happens on the writer. One way to do this is for the writer
to present an append-only log of updates which the readers can consume to invalidate their
caches and to push updates to listening clients or pushers.
Synapse already stores much of its data as an append-only log so that it can correctly respond
to /sync requests so the amount of code changes needed to expose the append-only log to
the readers should be fairly minimal.
Architecture
See tcp_replication.md
Motivation
Previously the workers used an HTTP long poll mechanism to get updates from the master,
which had the problem of causing a lot of duplicate work on the server. This TCP protocol
replaces those APIs with the aim of increased efficiency.
Overview
The protocol is based on fire and forget, line based commands. An example flow would be
(where '>' indicates master to worker and '<' worker to master flows):
The example shows the server accepting a new connection and sending its identity with the
SERVER command, followed by the client server to respond with the position of all streams.
The server then periodically sends RDATA commands which have the format RDATA
<stream_name> <instance_name> <token> <row> , where the format of <row> is defined by the
individual streams. The <instance_name> is the name of the Synapse process that generated
the data (usually "master").
Error reporting happens by either the client or server sending an ERROR command, and usually
the connection will be closed.
Since the protocol is a simple line based, its possible to manually connect to the server using a
tool like netcat. A few things should be noted when manually using the protocol:
The federation stream is only available if federation sending has been disabled on the
main process.
The server will only time connections out that have sent a PING command. If a ping is
sent then the connection will be closed if no further commands are receieved within 15s.
Both the client and server protocol implementations will send an initial PING on
connection and ensure at least one command every 5s is sent (not necessarily PING ).
RDATA commands usually include a numeric token, however if the stream has multiple
rows to replicate per token the server will send multiple RDATA commands, with all but
the last having a token of batch . See the documentation on [Link] for
further details.
Architecture
The basic structure of the protocol is line based, where the initial word of each line specifies the
command. The rest of the line is parsed based on the command. For example, the RDATA
command is defined as:
(Note that <row_json> may contains spaces, but cannot contain newlines.)
Keep alives
Both sides are expected to send at least one command every 5s or so, and should send a PING
command if necessary. If either side do not receive a command within e.g. 15s then the
connection should be closed.
Because the server may be connected to manually using e.g. netcat, the timeouts aren't
enabled until an initial PING command is seen. Both the client and server implementations
below send a PING command immediately on connection to ensure the timeouts are enabled.
This ensures that both sides can quickly realize if the tcp connection has gone and handle the
situation appropriately.
Start up
Sends a SERVER command, which includes the identity of the server, allowing the client to
detect if its connected to the expected server
Sends a PING command as above, to enable the client to time out connections promptly.
The client:
Sends a NAME command, allowing the server to associate a human friendly name with the
connection. This is optional.
Sends a PING as above
Sends a REPLICATE to get the current position of all streams.
On receipt of a SERVER command, checks that the server name matches the expected
server name.
Error handling
If either side detects an error it can send an ERROR command and close the connection.
If the client side loses the connection to the server it should reconnect, following the steps
above.
Congestion
If the server sends messages faster than the client can consume them the server will first
buffer a (fairly large) number of commands and then disconnect the client. This ensures that
we don't queue up an unbounded number of commands in memory and gives us a potential
oppurtunity to squawk loudly. When/if the client recovers it can reconnect to the server and ask
for missed messages.
Reliability
In general the replication stream should be considered an unreliable transport since e.g.
commands are not resent if the connection disappears.
The exception to that are the replication streams, i.e. RDATA commands, since these include
tokens which can be used to restart the stream on connection errors.
The client should keep track of the token in the last RDATA command 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
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:
* connection established *
> SERVER localhost:8823
> PING 1490197665618
< NAME [Link]
< PING 1490197665618
< REPLICATE
> POSITION events master 1 1
> POSITION backfill master 1 1
> POSITION caches master 1 1
> RDATA caches master 2 ["get_user_by_id",["@01register-
user:localhost:8823"],1490197670513]
> RDATA events master 14 ["$149019767112vOHxz:localhost:8823",
"!AFDCvgApUmpdfVjIXm:localhost:8823","[Link].guest_access","",null]
< PING 1490197675618
> ERROR server stopping
* connection closed by server *
The POSITION command sent by the server is used to set the clients position without needing
to send data with the RDATA command.
In this case the client shouldn't advance their caches token until it sees the the last RDATA .
List of commands
The list of valid commands, with which side can send it: server (S) or client (C):
SERVER (S)
RDATA (S)
On receipt of a POSITION command clients should check if they have missed any updates, and
if so then fetch them out of band. Sent in response to a REPLICATE command (but can happen
at any time).
The POSITION command includes the source of the stream. Currently all streams are written by
a single process (usually "master"). If fetching missing updates via HTTP API, rather than via the
DB, then processes should make the request to the appropriate process.
Two positions are included, the "new" position and the last position sent respectively. This
allows servers to tell instances that the positions have advanced but no data has been written,
without clients needlessly checking to see if they have missed any updates.
ERROR (S, C)
PING (S, C)
NAME (C)
Sent at the start by client to inform the server who they are
REPLICATE (C)
USER_SYNC (C)
CLEAR_USER_SYNC (C)
The server should clear all associated user sync data from the worker.
REMOTE_SERVER_UP (S, C)
Inform other processes that a remote server may have come back online.
The cache invalidation stream is used to inform workers when they need to invalidate any of
their caches in the data store. This is done by streaming all cache invalidations done on master
down to the workers, assuming that any caches on the workers also exist on the master.
Each individual cache invalidation results in a row being sent down replication, which includes
the cache name (the name of the function) and they key to invalidate. For example:
Alternatively, an entire cache can be invalidated by sending down a null instead of the key.
For example:
However, there are times when a number of caches need to be invalidated at the same time
with the same key. To reduce traffic we batch those invalidations into a single poke by defining
a special cache name that workers understand to mean to expand to invalidate the correct
caches.
Currently the special cache names are declared in synapse/storage/_base.py and are:
If a developer is planning to make a change to a feature of Synapse, it can be useful for general
documentation of how that feature is implemented to be available. This saves the developer
time in place of needing to understand how the feature works by reading the code.
Documentation that would be more useful for the perspective of a system administrator,
rather than a developer who's intending to change to code, should instead be placed under the
Usage section of the documentation.
How to test SAML as a developer without a
server
[Link] ([Link] is a great
resource for being able to tinker with the SAML options within Synapse without needing to
deploy and configure a complicated software stack.
1. Use the [Link] URL above or deploy your own and visit the IdP Metadata tab.
2. Copy the XML to your clipboard.
3. On your Synapse server, create a new file [Link] next to your [Link]
with the XML from step 2 as the contents.
4. Edit your [Link] to include:
saml2_config:
sp_config:
allow_unknown_attributes: true # Works around a bug with AVA Hashes:
[Link]
metadata:
local: ["[Link]"]
public_baseurl: [Link]
6. Run apt-get install xmlsec1 and pip install --upgrade --force 'pysaml2>=4.5.0'
to ensure the dependencies are installed and ready to go.
7. Restart Synapse.
Then in Riot:
Prerequisites
1. Create a new virtualenv: python3 -m venv <your virtualenv>
2. Activate your virtualenv: source /path/to/your/virtualenv/bin/activate
3. Install Django and django-mama-cas:
You should now have a Django project configured to serve CAS authentication with a single
user created.
Configure Synapse (and Element) to use CAS
1. Modify your [Link] to enable CAS and point it to your locally running Django
test server:
cas_config:
enabled: true
server_url: "[Link]
service_url: "[Link]
#displayname_attribute: name
#required_attributes:
# name: value
2. Restart Synapse.
Note that the above configuration assumes the homeserver is running on port 8081 and that
the CAS server is on port 8000, both on localhost.
If you want to repeat this process you'll need to manually logout first:
1. [Link]
2. Click "logout" in the top right.
Auth Chain Difference Algorithm
The auth chain difference algorithm is used by V2 state resolution, where a naive
implementation can be a significant source of CPU and DB usage.
Definitions
A state set is a set of state events; e.g. the input of a state resolution algorithm is a collection of
state sets.
The auth chain of a set of events are all the events' auth events and their auth events,
recursively (i.e. the events reachable by walking the graph induced by an event's auth events
links).
The auth chain difference of a collection of state sets is the union minus the intersection of the
sets of auth chains corresponding to the state sets, i.e an event is in the auth chain difference if
it is reachable by walking the auth event graph from at least one of the state sets but not from
all of the state sets.
This can work well for state sets that have a small auth chain difference, but can be very
inefficient for larger differences. However, this algorithm is still used if we don't have a chain
cover index for the room (e.g. because we're in the process of indexing it).
1. A and B have the same chain ID and A 's sequence number is less than B 's sequence
number; or
2. there is a link L between B 's chain ID and A 's chain ID such that L.start_seq_no <=
B.seq_no and A.seq_no <= L.end_seq_no .
There are actually two potential implementations, one where we store links from each chain to
every other reachable chain (the transitive closure of the links 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
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 are those that would be remove in the
second implementation described above).
Note that we don't include all links between events and their auth events, as most of those
links would be redundant. For example, all events point to the create event, but each chain only
needs the one link from it's base to the create event.
Using the Index
This index can be used to calculate the auth chain difference of the state sets by looking at the
chain ID and sequence numbers reachable from each state set:
1. For every state set lookup the chain ID/sequence numbers of each state event
2. Use the index to find all chains and the maximum sequence number reachable from each
state set.
3. The auth chain difference is then all events in each chain that have sequence numbers
between the maximum sequence number reachable from any state set and the minimum
reachable by all state sets (if any).
Note that steps 2 is effectively calculating the auth chain for each state set (in terms of chain
IDs and sequence numbers), and step 3 is calculating the difference between the union and
intersection of the auth chains.
Worked Example
For example, given the above graph, we can calculate the difference between state sets
consisting of:
Using the index we see that the following auth chains are reachable from each state set:
And so, for each the ranges that are in the auth chain difference:
So the final result is: Bob's second join (2,2) , the second power level (3,2) and both of
Alice's joins (4,2) & (4,3) .
Media Repository
Synapse implementation-specific details for the media repository
The media repository is where attachments and avatar photos are stored. It stores attachment
content and thumbnails for media uploaded by local users. It caches attachment content and
thumbnails for media uploaded by remote users.
Storage
Each item of media is assigned a media_id when it is uploaded. The media_id is a randomly
chosen, URL safe 24 character string.
Metadata such as the MIME type, upload time and length are stored in the sqlite3 database
indexed by media_id .
Remote content is cached under "remote_content" directory. Each item of remote content is
assigned a local "filesystem_id" to ensure that the directory structure
"remote_content/server_name/aa/bb/ccccccccdddddddddddd" is appropriate. Thumbnails for
remote content are stored under "remote_thumbnails/server_name/..."
Room and User Statistics
Synapse maintains room and user statistics (as well as a cache of room state), in various tables.
These can be used for administrative purposes but are also used when generating the public
room directory.
High-Level Concepts
Definitions
Overview
Stats are maintained as time series. There are two kinds of column:
absolute columns – where the value is correct for the time given by end_ts in the stats
row. (Imagine a line graph for these values)
They can also be thought of as 'gauges' in Prometheus, if you are familiar.
per-slice columns – where the value corresponds to how many of the occurrences
occurred within the time slice given by (end_ts − bucket_size)…end_ts or start_ts…
end_ts . (Imagine a histogram for these values)
Stats are maintained in two tables (for each type): current and historical.
Current stats correspond to the present values. Each subject can only have one entry.
Historical stats correspond to values in the past. Subjects may have multiple entries.
Concepts around the management of stats
Current rows
Current rows contain the most up-to-date statistics for a room. They only contain absolute
columns
Historical rows
Historical rows can always be considered to be valid for the time slice and end time specified.
historical rows will not exist for every time slice – they will be omitted if there were no
changes. In this case, the following assumptions can be made to interpolate/recreate
missing rows:
absolute fields have the same values as in the preceding row
per-slice fields are zero ( 0 )
historical rows will not be retained forever – rows older than a configurable time will be
purged.
Purge
Policy
Synapse follows the upstream support life cycles for Python and PostgreSQL, i.e. when a
version reaches End of Life Synapse will withdraw support for that version in future releases.
Details on the upstream support life cycles for Python and PostgreSQL are documented at
[Link] and [Link]
Context
It is important for system admins to have a clear understanding of the platform requirements
of Synapse and its deprecation policies so that they can effectively plan upgrading their
infrastructure ahead of time. This is especially important in contexts where upgrading the
infrastructure requires auditing and approval from a security team, or where otherwise
upgrading is a long process.
By following the upstream support life cycles Synapse can ensure that its dependencies
continue to get security patches, while not requiring system admins to constantly update their
platform dependencies to the latest versions.
Setting up a reverse proxy for Synapse involves directing federation traffic through the proxy, which allows for easier TLS certificate management since it eliminates the need for Synapse to handle TLS directly. This is done by setting the `no_tls` option to `True` in the Synapse configuration when using a reverse proxy . This configuration is recommended because it simplifies TLS certificate management and reduces the load on Synapse, as the reverse proxy handles incoming connections and presents the TLS certificates, which might be more efficient than Synapse's internal handling .
Administrators can customize the SSO mapping providers for OpenID and SAML by specifying a custom mapping module in the configuration. For OpenID, this involves setting the `oidc_config.user_mapping_provider.module` and providing specific configuration options under `oidc_config.user_mapping_provider.config` . SAML mappings can be adjusted similarly by editing the `saml2_config.user_mapping_provider.module`. Custom modules must implement specific methods like `map_user_attributes` and `get_extra_attributes` to extract and map user information based on claims received during authentication .
A Synapse administrator might customize the default SSO user mapping provider to ensure that the user's attributes from an SSO identity provider are correctly mapped to Synapse's internal identifiers and attributes. This customization can be achieved by specifying a different mapping provider module through the configuration, replacing or extending the default options with settings specific to their needs . Customization might involve handling specific user claims or meeting organizational requirements for user profiles and login procedures .
Synapse provides configurable retention and redaction policies which allow server and room admins to control the duration of data storage. Default retention policies apply to rooms lacking specific settings, with configurable `min_lifetime` and `max_lifetime` options . Redacted events can be kept temporarily in an unredacted form, with a default retention period of `7d`, which can be adjusted or disabled by setting it to `null` . These options help balance privacy with operational requirements.
User deactivation in Synapse involves several actions such as unbinding and removing 3PIDs from the identity server, deleting all devices and E2EE keys, removing the user from rooms, and rejecting all pending invites . If the `erase` parameter is set to `true`, additional actions are performed, including removing the user's display name, avatar URL, and marking the user as erased . This thorough process ensures both data privacy and system integrity.
When upgrading Synapse installed using prebuilt packages, it is important to first check if there are any special upgrade steps required for the current version . The process generally follows the standard upgrade path of the distribution's package management system. It is also crucial to verify that the versions of Python and PostgreSQL in use are still supported, as Synapse aligns with upstream lifecycles and may drop support for outdated versions . This ensures compatibility and security with new releases.
To ensure Synapse's federation setup communicates correctly with other servers, configure the `server_name` in the `homeserver.yaml` to match your server's public DNS hostname and ensure listening for TLS connections on port 8448. Using a reverse proxy to manage TLS on this port is the preferred method . Additionally, configuration for delegation can be used if the domain's server doesn't directly serve Synapse, allowing admins to route traffic as needed . Using tools like the federation tester can help verify configuration correctness .
Using a reverse proxy for TLS traffic management minimizes the need for Synapse to handle TLS certificates directly. When a reverse proxy handles all TLS traffic, Synapse can operate with `no_tls: True` in the configuration, meaning it no longer needs the certificates for most operations . This setup is advantageous because it streamlines server maintenance and allows centralized certificate management through the reverse proxy. Synapse will still require some certificates to populate legacy fields in older federation APIs, but this is generally handled through modern setups .
Configuring auto-join rooms for new users in Synapse involves specifying room aliases and ensuring they are publicly joinable . It's important to determine whether these rooms should be federated with other servers. By default, auto-created rooms are publicly joinable and federated, but settings exist to restrict this by setting `autocreate_auto_join_rooms_federated` to `false` if desired . These settings help manage user experience and server load while considering the reach of the hosted rooms.
For production Synapse deployments, PostgreSQL is preferred over SQLite due to significant performance improvements. PostgreSQL provides superior threading and caching models and includes a smarter query optimizer, allowing it to run on separate hardware . SQLite, on the other hand, is mainly suitable for testing purposes as it may lead to poor performance, especially when participating in large rooms .