mirror of
https://github.com/element-hq/synapse.git
synced 2025-07-01 00:00:19 -04:00
Compare commits
238 Commits
v1.126.0rc
...
develop
Author | SHA1 | Date | |
---|---|---|---|
|
a2bee2f255 | ||
|
3878699df7 | ||
|
b35c6483d5 | ||
|
bfb3a6e700 | ||
|
8afea3d51d | ||
|
db710cf29b | ||
|
de29c13d41 | ||
|
434e38941a | ||
|
b1396475c4 | ||
|
b088194f48 | ||
|
2f21b27465 | ||
|
3807fd42e1 | ||
|
99474e7fdf | ||
|
ec13ed4169 | ||
|
62b5b0b962 | ||
|
0779587f9f | ||
|
0c7d9919fa | ||
|
6fabf82f4f | ||
|
cb259eb206 | ||
|
6791e6e250 | ||
|
3cabaa84ca | ||
|
74ca7ae720 | ||
|
5102565369 | ||
|
33e0c25279 | ||
|
73a38384f5 | ||
|
4a803e8257 | ||
|
51dbbbb40f | ||
|
6363d63822 | ||
|
d1139ebfc1 | ||
|
3e571561c9 | ||
|
a3b80071cd | ||
|
f500c7d982 | ||
|
df04931f0b | ||
|
f56670515b | ||
|
db8a8d33fe | ||
|
3b94e40cc8 | ||
|
6b1e3c9c66 | ||
|
1709957395 | ||
|
0de7aa9953 | ||
|
e4ca593eb6 | ||
|
978032141b | ||
|
142ba5df89 | ||
|
eb5dfc19e5 | ||
|
cc6b4980ef | ||
|
d5da07703d | ||
|
96c556081a | ||
|
5581fbb906 | ||
|
1ab35a0a78 | ||
|
341d956ee6 | ||
|
6521406a37 | ||
|
6e600c986e | ||
|
d285d76185 | ||
|
919c362466 | ||
|
82189cbde4 | ||
|
e80bc4b062 | ||
|
865d43b4b3 | ||
|
0b9f1757a7 | ||
|
8010377a88 | ||
|
586b82e580 | ||
|
9b2bc75ed4 | ||
|
28f21b4036 | ||
|
379356c0ea | ||
|
fbe7a898f0 | ||
|
08a0506f48 | ||
|
c47d8e0ee1 | ||
|
a4d8da7a1b | ||
|
fae72f181b | ||
|
2436512a25 | ||
|
461571fcf2 | ||
|
22db145da3 | ||
|
d82ad6e554 | ||
|
58e8521313 | ||
|
3680de63a7 | ||
|
c8733be8aa | ||
|
07468a0f1c | ||
|
33ba8860c4 | ||
|
24e849e483 | ||
|
1624073191 | ||
|
ed6b7ba9c3 | ||
|
b7d4841947 | ||
|
553e124f76 | ||
|
99cbd33630 | ||
|
4b1d9d5d0e | ||
|
f92c6455ef | ||
|
a36f3a6d87 | ||
|
9d43bec326 | ||
|
a6cb3533db | ||
|
303c5c4daa | ||
|
1f4ae2f9eb | ||
|
67920c0aca | ||
|
17e6b32966 | ||
|
afeb0e01c5 | ||
|
cd1a3ac584 | ||
|
b3b24c69fc | ||
|
fa4a00a2da | ||
|
7d4c3b64e3 | ||
|
078cefd014 | ||
|
74e2f028bb | ||
|
0afdc0fc7f | ||
|
f5ed52c1e2 | ||
|
44ae5362fd | ||
|
194b923a6e | ||
|
a3bbd7eeab | ||
|
6e910e2b2c | ||
|
2db54c88ff | ||
|
480d4faa38 | ||
|
ba2f1be891 | ||
|
c626d54cea | ||
|
99c15f4630 | ||
|
09b4109c2e | ||
|
40ce11ded0 | ||
|
3dade08e7c | ||
|
1920dfff40 | ||
|
b7728a2df1 | ||
|
c6dfe70014 | ||
|
b5d94f654c | ||
|
7c633f1a58 | ||
|
ae877aa101 | ||
|
740fc885cd | ||
|
9a62b2d47a | ||
|
d0873d549a | ||
|
c9adbc6a1c | ||
|
9f9eb56333 | ||
|
fe8bb620de | ||
|
b8146d4b03 | ||
|
411d239db4 | ||
|
d18edf67d6 | ||
|
fd5d3d852d | ||
|
ea376126a0 | ||
|
74be5cfdbc | ||
|
f2ca2e31f7 | ||
|
6dc1ecd359 | ||
|
2965c9970c | ||
|
d59bbd8b6b | ||
|
7be6c711d4 | ||
|
5ab05e7b95 | ||
|
7563b2a2a3 | ||
|
4097ada89f | ||
|
f79811ed80 | ||
|
5f587dfd38 | ||
|
a4ec96ca34 | ||
|
02dca7c67a | ||
|
dbf5b0be67 | ||
|
b2f12d22e4 | ||
|
4eaab31757 | ||
|
ad140130cc | ||
|
e47de2b32d | ||
|
0384fd72ee | ||
|
75832f25b0 | ||
|
7346760aed | ||
|
b0795d0cb6 | ||
|
2ef7824620 | ||
|
39e17856a3 | ||
|
4c958c679a | ||
|
a87981f673 | ||
|
2ff977a6c3 | ||
|
1482ad1917 | ||
|
5b89c92643 | ||
|
33824495ba | ||
|
89cb613a4e | ||
|
d67e9c5367 | ||
|
2b5c6239de | ||
|
c16a981f22 | ||
|
0046d7278b | ||
|
9b8eebbe4e | ||
|
5ced4efe1d | ||
|
2c7a61e311 | ||
|
45420b1d42 | ||
|
19b0e23c3d | ||
|
a832375bfb | ||
|
ae701e1709 | ||
|
dd05cc55ee | ||
|
081f6ad50f | ||
|
b30fcb03cc | ||
|
0e3c0aeee8 | ||
|
5c84f25809 | ||
|
770768614b | ||
|
b8b3896b1d | ||
|
01efc49554 | ||
|
fa53a8512a | ||
|
fdbcb821ff | ||
|
8eb991b746 | ||
|
87d374c639 | ||
|
1709234311 | ||
|
80b62d7903 | ||
|
7ace290f07 | ||
|
2f812c2eb6 | ||
|
90f346183a | ||
|
f638a76ba4 | ||
|
cf02b8fea5 | ||
|
1deb6e03e0 | ||
|
02eed668b8 | ||
|
9f8ed14535 | ||
|
3bc04d05a4 | ||
|
4dba011c31 | ||
|
76ffd3ba01 | ||
|
3c188231c7 | ||
|
d17295e5c3 | ||
|
a39b856cf0 | ||
|
2830013e5e | ||
|
ecc09b15f1 | ||
|
31110f35d9 | ||
|
2277df2a1e | ||
|
5e83434f3a | ||
|
a227d20c25 | ||
|
bd08a01fc8 | ||
|
92a29dcffc | ||
|
2719bd1794 | ||
|
7af299b365 | ||
|
d8fef721a0 | ||
|
1efb826b54 | ||
|
33bcef9dc7 | ||
|
51deadec41 | ||
|
47e295bf3a | ||
|
4b8dbe22c0 | ||
|
bfafd0f2c7 | ||
|
d61bdff7a4 | ||
|
4d2c4ce92b | ||
|
79081e1be5 | ||
|
51df675c05 | ||
|
59a15da433 | ||
|
a278c0d852 | ||
|
929f19b472 | ||
|
60b3cd0650 | ||
|
df044a3667 | ||
|
04814a48de | ||
|
698278ba50 | ||
|
74cc353961 | ||
|
caa2012154 | ||
|
5064f35958 | ||
|
c30157b3cb | ||
|
fda1ffe5b8 | ||
|
a4c476305e | ||
|
1803a62db4 | ||
|
8295de87a7 | ||
|
350e84a8a4 | ||
|
69aceef8f6 | ||
|
b7946c29be |
10
.ci/before_build_wheel.sh
Normal file
10
.ci/before_build_wheel.sh
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -xeu
|
||||||
|
|
||||||
|
# On 32-bit Linux platforms, we need libatomic1 to use rustup
|
||||||
|
if command -v yum &> /dev/null; then
|
||||||
|
yum install -y libatomic
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install a Rust toolchain
|
||||||
|
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain 1.82.0 -y --profile minimal
|
@ -11,12 +11,12 @@ with open("poetry.lock", "rb") as f:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
lock_version = lockfile["metadata"]["lock-version"]
|
lock_version = lockfile["metadata"]["lock-version"]
|
||||||
assert lock_version == "2.0"
|
assert lock_version == "2.1"
|
||||||
except Exception:
|
except Exception:
|
||||||
print(
|
print(
|
||||||
"""\
|
"""\
|
||||||
Lockfile is not version 2.0. You probably need to upgrade poetry on your local box
|
Lockfile is not version 2.1. You probably need to upgrade poetry on your local box
|
||||||
and re-run `poetry lock --no-update`. See the Poetry cheat sheet at
|
and re-run `poetry lock`. See the Poetry cheat sheet at
|
||||||
https://element-hq.github.io/synapse/develop/development/dependencies.html
|
https://element-hq.github.io/synapse/develop/development/dependencies.html
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -9,5 +9,4 @@
|
|||||||
- End with either a period (.) or an exclamation mark (!).
|
- End with either a period (.) or an exclamation mark (!).
|
||||||
- Start with a capital letter.
|
- Start with a capital letter.
|
||||||
- Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry.
|
- Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry.
|
||||||
* [ ] [Code style](https://element-hq.github.io/synapse/latest/code_style.html) is correct
|
* [ ] [Code style](https://element-hq.github.io/synapse/latest/code_style.html) is correct (run the [linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))
|
||||||
(run the [linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))
|
|
||||||
|
16
.github/workflows/docker.yml
vendored
16
.github/workflows/docker.yml
vendored
@ -18,22 +18,22 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
id: qemu
|
id: qemu
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||||
with:
|
with:
|
||||||
platforms: arm64
|
platforms: arm64
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
|
|
||||||
- name: Inspect builder
|
- name: Inspect builder
|
||||||
run: docker buildx inspect
|
run: docker buildx inspect
|
||||||
|
|
||||||
- name: Install Cosign
|
- name: Install Cosign
|
||||||
uses: sigstore/cosign-installer@v3.8.1
|
uses: sigstore/cosign-installer@fb28c2b6339dcd94da6e4cbcbc5e888961f6f8c3 # v3.9.0
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Extract version from pyproject.toml
|
- name: Extract version from pyproject.toml
|
||||||
# Note: explicitly requesting bash will mean bash is invoked with `-eo pipefail`, see
|
# Note: explicitly requesting bash will mean bash is invoked with `-eo pipefail`, see
|
||||||
@ -43,13 +43,13 @@ jobs:
|
|||||||
echo "SYNAPSE_VERSION=$(grep "^version" pyproject.toml | sed -E 's/version\s*=\s*["]([^"]*)["]/\1/')" >> $GITHUB_ENV
|
echo "SYNAPSE_VERSION=$(grep "^version" pyproject.toml | sed -E 's/version\s*=\s*["]([^"]*)["]/\1/')" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Log in to DockerHub
|
- name: Log in to DockerHub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Log in to GHCR
|
- name: Log in to GHCR
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@ -57,7 +57,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Calculate docker image tag
|
- name: Calculate docker image tag
|
||||||
id: set-tag
|
id: set-tag
|
||||||
uses: docker/metadata-action@master
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
docker.io/matrixdotorg/synapse
|
docker.io/matrixdotorg/synapse
|
||||||
@ -72,7 +72,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push all platforms
|
- name: Build and push all platforms
|
||||||
id: build-and-push
|
id: build-and-push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
labels: |
|
labels: |
|
||||||
|
4
.github/workflows/docs-pr-netlify.yaml
vendored
4
.github/workflows/docs-pr-netlify.yaml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
# There's a 'download artifact' action, but it hasn't been updated for the workflow_run action
|
# There's a 'download artifact' action, but it hasn't been updated for the workflow_run action
|
||||||
# (https://github.com/actions/download-artifact/issues/60) so instead we get this mess:
|
# (https://github.com/actions/download-artifact/issues/60) so instead we get this mess:
|
||||||
- name: 📥 Download artifact
|
- name: 📥 Download artifact
|
||||||
uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
|
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11
|
||||||
with:
|
with:
|
||||||
workflow: docs-pr.yaml
|
workflow: docs-pr.yaml
|
||||||
run_id: ${{ github.event.workflow_run.id }}
|
run_id: ${{ github.event.workflow_run.id }}
|
||||||
@ -22,7 +22,7 @@ jobs:
|
|||||||
path: book
|
path: book
|
||||||
|
|
||||||
- name: 📤 Deploy to Netlify
|
- name: 📤 Deploy to Netlify
|
||||||
uses: matrix-org/netlify-pr-preview@v3
|
uses: matrix-org/netlify-pr-preview@9805cd123fc9a7e421e35340a05e1ebc5dee46b5 # v3
|
||||||
with:
|
with:
|
||||||
path: book
|
path: book
|
||||||
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
|
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
|
||||||
|
8
.github/workflows/docs-pr.yaml
vendored
8
.github/workflows/docs-pr.yaml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
name: GitHub Pages
|
name: GitHub Pages
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
# Fetch all history so that the schema_versions script works.
|
# Fetch all history so that the schema_versions script works.
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@ -24,7 +24,7 @@ jobs:
|
|||||||
mdbook-version: '0.4.17'
|
mdbook-version: '0.4.17'
|
||||||
|
|
||||||
- name: Setup python
|
- name: Setup python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ jobs:
|
|||||||
cp book/welcome_and_overview.html book/index.html
|
cp book/welcome_and_overview.html book/index.html
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: book
|
name: book
|
||||||
path: book
|
path: book
|
||||||
@ -50,7 +50,7 @@ jobs:
|
|||||||
name: Check links in documentation
|
name: Check links in documentation
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup mdbook
|
- name: Setup mdbook
|
||||||
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2.0.0
|
uses: peaceiris/actions-mdbook@ee69d230fe19748b7abf22df32acaa93833fad08 # v2.0.0
|
||||||
|
16
.github/workflows/docs.yaml
vendored
16
.github/workflows/docs.yaml
vendored
@ -50,7 +50,7 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- pre
|
- pre
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
# Fetch all history so that the schema_versions script works.
|
# Fetch all history so that the schema_versions script works.
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@ -64,7 +64,7 @@ jobs:
|
|||||||
run: echo 'window.SYNAPSE_VERSION = "${{ needs.pre.outputs.branch-version }}";' > ./docs/website_files/version.js
|
run: echo 'window.SYNAPSE_VERSION = "${{ needs.pre.outputs.branch-version }}";' > ./docs/website_files/version.js
|
||||||
|
|
||||||
- name: Setup python
|
- name: Setup python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
@ -78,6 +78,18 @@ jobs:
|
|||||||
mdbook build
|
mdbook build
|
||||||
cp book/welcome_and_overview.html book/index.html
|
cp book/welcome_and_overview.html book/index.html
|
||||||
|
|
||||||
|
- name: Prepare and publish schema files
|
||||||
|
run: |
|
||||||
|
sudo apt-get update && sudo apt-get install -y yq
|
||||||
|
mkdir -p book/schema
|
||||||
|
# Remove developer notice before publishing.
|
||||||
|
rm schema/v*/Do\ not\ edit\ files\ in\ this\ folder
|
||||||
|
# Copy schema files that are independent from current Synapse version.
|
||||||
|
cp -r -t book/schema schema/v*/
|
||||||
|
# Convert config schema from YAML source file to JSON.
|
||||||
|
yq < schema/synapse-config.schema.yaml \
|
||||||
|
> book/schema/synapse-config.schema.json
|
||||||
|
|
||||||
# Deploy to the target directory.
|
# Deploy to the target directory.
|
||||||
- name: Deploy to gh pages
|
- name: Deploy to gh pages
|
||||||
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
|
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
|
||||||
|
11
.github/workflows/fix_lint.yaml
vendored
11
.github/workflows/fix_lint.yaml
vendored
@ -13,21 +13,22 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: dtolnay/rust-toolchain@56f84321dbccf38fb67ce29ab63e4754056677e0 # master (rust 1.85.1)
|
||||||
with:
|
with:
|
||||||
# We use nightly so that `fmt` correctly groups together imports, and
|
# We use nightly so that `fmt` correctly groups together imports, and
|
||||||
# clippy correctly fixes up the benchmarks.
|
# clippy correctly fixes up the benchmarks.
|
||||||
toolchain: nightly-2022-12-01
|
toolchain: nightly-2022-12-01
|
||||||
components: clippy, rustfmt
|
components: clippy, rustfmt
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- name: Setup Poetry
|
- name: Setup Poetry
|
||||||
uses: matrix-org/setup-python-poetry@v1
|
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
install-project: "false"
|
install-project: "false"
|
||||||
|
poetry-version: "2.1.1"
|
||||||
|
|
||||||
- name: Run ruff check
|
- name: Run ruff check
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
@ -43,6 +44,6 @@ jobs:
|
|||||||
- run: cargo fmt
|
- run: cargo fmt
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
- uses: stefanzweifel/git-auto-commit-action@b863ae1933cb653a53c021fe36dbb774e1fb9403 # v5.2.0
|
||||||
with:
|
with:
|
||||||
commit_message: "Attempt to fix linting"
|
commit_message: "Attempt to fix linting"
|
||||||
|
34
.github/workflows/latest_deps.yml
vendored
34
.github/workflows/latest_deps.yml
vendored
@ -39,17 +39,17 @@ jobs:
|
|||||||
if: needs.check_repo.outputs.should_run_workflow == 'true'
|
if: needs.check_repo.outputs.should_run_workflow == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@fcf085fcb4b4b8f63f96906cd713eb52181b5ea4 # stable (rust 1.85.1)
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
# The dev dependencies aren't exposed in the wheel metadata (at least with current
|
# The dev dependencies aren't exposed in the wheel metadata (at least with current
|
||||||
# poetry-core versions), so we install with poetry.
|
# poetry-core versions), so we install with poetry.
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
poetry-version: "1.3.2"
|
poetry-version: "2.1.1"
|
||||||
extras: "all"
|
extras: "all"
|
||||||
# Dump installed versions for debugging.
|
# Dump installed versions for debugging.
|
||||||
- run: poetry run pip list > before.txt
|
- run: poetry run pip list > before.txt
|
||||||
@ -72,11 +72,11 @@ jobs:
|
|||||||
postgres-version: "14"
|
postgres-version: "14"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@fcf085fcb4b4b8f63f96906cd713eb52181b5ea4 # stable (rust 1.85.1)
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- run: sudo apt-get -qq install xmlsec1
|
- run: sudo apt-get -qq install xmlsec1
|
||||||
- name: Set up PostgreSQL ${{ matrix.postgres-version }}
|
- name: Set up PostgreSQL ${{ matrix.postgres-version }}
|
||||||
@ -86,7 +86,7 @@ jobs:
|
|||||||
-e POSTGRES_PASSWORD=postgres \
|
-e POSTGRES_PASSWORD=postgres \
|
||||||
-e POSTGRES_INITDB_ARGS="--lc-collate C --lc-ctype C --encoding UTF8" \
|
-e POSTGRES_INITDB_ARGS="--lc-collate C --lc-ctype C --encoding UTF8" \
|
||||||
postgres:${{ matrix.postgres-version }}
|
postgres:${{ matrix.postgres-version }}
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- run: pip install .[all,test]
|
- run: pip install .[all,test]
|
||||||
@ -145,11 +145,11 @@ jobs:
|
|||||||
BLACKLIST: ${{ matrix.workers && 'synapse-blacklist-with-workers' }}
|
BLACKLIST: ${{ matrix.workers && 'synapse-blacklist-with-workers' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@fcf085fcb4b4b8f63f96906cd713eb52181b5ea4 # stable (rust 1.85.1)
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- name: Ensure sytest runs `pip install`
|
- name: Ensure sytest runs `pip install`
|
||||||
# Delete the lockfile so sytest will `pip install` rather than `poetry install`
|
# Delete the lockfile so sytest will `pip install` rather than `poetry install`
|
||||||
@ -164,7 +164,7 @@ jobs:
|
|||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
run: /sytest/scripts/tap_to_gha.pl /logs/results.tap
|
run: /sytest/scripts/tap_to_gha.pl /logs/results.tap
|
||||||
- name: Upload SyTest logs
|
- name: Upload SyTest logs
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: Sytest Logs - ${{ job.status }} - (${{ join(matrix.*, ', ') }})
|
name: Sytest Logs - ${{ job.status }} - (${{ join(matrix.*, ', ') }})
|
||||||
@ -192,15 +192,15 @@ jobs:
|
|||||||
database: Postgres
|
database: Postgres
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Run actions/checkout@v4 for synapse
|
- name: Check out synapse codebase
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
path: synapse
|
path: synapse
|
||||||
|
|
||||||
- name: Prepare Complement's Prerequisites
|
- name: Prepare Complement's Prerequisites
|
||||||
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
cache-dependency-path: complement/go.sum
|
cache-dependency-path: complement/go.sum
|
||||||
go-version-file: complement/go.mod
|
go-version-file: complement/go.mod
|
||||||
@ -225,7 +225,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2
|
- uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
4
.github/workflows/poetry_lockfile.yaml
vendored
4
.github/workflows/poetry_lockfile.yaml
vendored
@ -16,8 +16,8 @@ jobs:
|
|||||||
name: "Check locked dependencies have sdists"
|
name: "Check locked dependencies have sdists"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- run: pip install tomli
|
- run: pip install tomli
|
||||||
|
10
.github/workflows/push_complement_image.yml
vendored
10
.github/workflows/push_complement_image.yml
vendored
@ -33,29 +33,29 @@ jobs:
|
|||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout specific branch (debug build)
|
- name: Checkout specific branch (debug build)
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
if: github.event_name == 'workflow_dispatch'
|
if: github.event_name == 'workflow_dispatch'
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.branch }}
|
ref: ${{ inputs.branch }}
|
||||||
- name: Checkout clean copy of develop (scheduled build)
|
- name: Checkout clean copy of develop (scheduled build)
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
if: github.event_name == 'schedule'
|
if: github.event_name == 'schedule'
|
||||||
with:
|
with:
|
||||||
ref: develop
|
ref: develop
|
||||||
- name: Checkout clean copy of master (on-push)
|
- name: Checkout clean copy of master (on-push)
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
- name: Login to registry
|
- name: Login to registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Work out labels for complement image
|
- name: Work out labels for complement image
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository }}/complement-synapse
|
images: ghcr.io/${{ github.repository }}/complement-synapse
|
||||||
tags: |
|
tags: |
|
||||||
|
34
.github/workflows/release-artifacts.yml
vendored
34
.github/workflows/release-artifacts.yml
vendored
@ -27,8 +27,8 @@ jobs:
|
|||||||
name: "Calculate list of debian distros"
|
name: "Calculate list of debian distros"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- id: set-distros
|
- id: set-distros
|
||||||
@ -55,18 +55,18 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
path: src
|
path: src
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
with:
|
with:
|
||||||
install: true
|
install: true
|
||||||
|
|
||||||
- name: Set up docker layer caching
|
- name: Set up docker layer caching
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||||
with:
|
with:
|
||||||
path: /tmp/.buildx-cache
|
path: /tmp/.buildx-cache
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
@ -74,7 +74,7 @@ jobs:
|
|||||||
${{ runner.os }}-buildx-
|
${{ runner.os }}-buildx-
|
||||||
|
|
||||||
- name: Set up python
|
- name: Set up python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ jobs:
|
|||||||
echo "ARTIFACT_NAME=${DISTRO#*:}" >> "$GITHUB_OUTPUT"
|
echo "ARTIFACT_NAME=${DISTRO#*:}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Upload debs as artifacts
|
- name: Upload debs as artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: debs-${{ steps.artifact-name.outputs.ARTIFACT_NAME }}
|
name: debs-${{ steps.artifact-name.outputs.ARTIFACT_NAME }}
|
||||||
path: debs/*
|
path: debs/*
|
||||||
@ -130,20 +130,20 @@ jobs:
|
|||||||
arch: aarch64
|
arch: aarch64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
# setup-python@v4 doesn't impose a default python version. Need to use 3.x
|
# setup-python@v4 doesn't impose a default python version. Need to use 3.x
|
||||||
# here, because `python` on osx points to Python 2.7.
|
# here, because `python` on osx points to Python 2.7.
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
|
|
||||||
- name: Install cibuildwheel
|
- name: Install cibuildwheel
|
||||||
run: python -m pip install cibuildwheel==2.19.1
|
run: python -m pip install cibuildwheel==2.23.0
|
||||||
|
|
||||||
- name: Set up QEMU to emulate aarch64
|
- name: Set up QEMU to emulate aarch64
|
||||||
if: matrix.arch == 'aarch64'
|
if: matrix.arch == 'aarch64'
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||||
with:
|
with:
|
||||||
platforms: arm64
|
platforms: arm64
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ jobs:
|
|||||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||||
CIBW_ENVIRONMENT_PASS_LINUX: CARGO_NET_GIT_FETCH_WITH_CLI
|
CIBW_ENVIRONMENT_PASS_LINUX: CARGO_NET_GIT_FETCH_WITH_CLI
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: Wheel-${{ matrix.os }}-${{ matrix.arch }}
|
name: Wheel-${{ matrix.os }}-${{ matrix.arch }}
|
||||||
path: ./wheelhouse/*.whl
|
path: ./wheelhouse/*.whl
|
||||||
@ -176,8 +176,8 @@ jobs:
|
|||||||
if: ${{ !startsWith(github.ref, 'refs/pull/') }}
|
if: ${{ !startsWith(github.ref, 'refs/pull/') }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ jobs:
|
|||||||
- name: Build sdist
|
- name: Build sdist
|
||||||
run: python -m build --sdist
|
run: python -m build --sdist
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: Sdist
|
name: Sdist
|
||||||
path: dist/*.tar.gz
|
path: dist/*.tar.gz
|
||||||
@ -203,7 +203,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Download all workflow run artifacts
|
- name: Download all workflow run artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
- name: Build a tarball for the debs
|
- name: Build a tarball for the debs
|
||||||
# We need to merge all the debs uploads into one folder, then compress
|
# We need to merge all the debs uploads into one folder, then compress
|
||||||
# that.
|
# that.
|
||||||
@ -213,7 +213,7 @@ jobs:
|
|||||||
tar -cvJf debs.tar.xz debs
|
tar -cvJf debs.tar.xz debs
|
||||||
- name: Attach to release
|
- name: Attach to release
|
||||||
# Pinned to work around https://github.com/softprops/action-gh-release/issues/445
|
# Pinned to work around https://github.com/softprops/action-gh-release/issues/445
|
||||||
uses: softprops/action-gh-release@v0.1.15
|
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v0.1.15
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
|
57
.github/workflows/schema.yaml
vendored
Normal file
57
.github/workflows/schema.yaml
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
name: Schema
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- schema/**
|
||||||
|
- docs/usage/configuration/config_documentation.md
|
||||||
|
push:
|
||||||
|
branches: ["develop", "release-*"]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-schema:
|
||||||
|
name: Ensure Synapse config schema is valid
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
|
with:
|
||||||
|
python-version: "3.x"
|
||||||
|
- name: Install check-jsonschema
|
||||||
|
run: pip install check-jsonschema==0.33.0
|
||||||
|
|
||||||
|
- name: Validate meta schema
|
||||||
|
run: check-jsonschema --check-metaschema schema/v*/meta.schema.json
|
||||||
|
- name: Validate schema
|
||||||
|
run: |-
|
||||||
|
# Please bump on introduction of a new meta schema.
|
||||||
|
LATEST_META_SCHEMA_VERSION=v1
|
||||||
|
check-jsonschema \
|
||||||
|
--schemafile="schema/$LATEST_META_SCHEMA_VERSION/meta.schema.json" \
|
||||||
|
schema/synapse-config.schema.yaml
|
||||||
|
- name: Validate default config
|
||||||
|
# Populates the empty instance with default values and checks against the schema.
|
||||||
|
run: |-
|
||||||
|
echo "{}" | check-jsonschema \
|
||||||
|
--fill-defaults --schemafile=schema/synapse-config.schema.yaml -
|
||||||
|
|
||||||
|
check-doc-generation:
|
||||||
|
name: Ensure generated documentation is up-to-date
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
|
with:
|
||||||
|
python-version: "3.x"
|
||||||
|
- name: Install PyYAML
|
||||||
|
run: pip install PyYAML==6.0.2
|
||||||
|
|
||||||
|
- name: Regenerate config documentation
|
||||||
|
run: |
|
||||||
|
scripts-dev/gen_config_documentation.py \
|
||||||
|
schema/synapse-config.schema.yaml \
|
||||||
|
> docs/usage/configuration/config_documentation.md
|
||||||
|
- name: Error in case of any differences
|
||||||
|
# Errors if there are now any modified files (untracked files are ignored).
|
||||||
|
run: 'git diff --exit-code'
|
154
.github/workflows/tests.yml
vendored
154
.github/workflows/tests.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
linting: ${{ !startsWith(github.ref, 'refs/pull/') || steps.filter.outputs.linting }}
|
linting: ${{ !startsWith(github.ref, 'refs/pull/') || steps.filter.outputs.linting }}
|
||||||
linting_readme: ${{ !startsWith(github.ref, 'refs/pull/') || steps.filter.outputs.linting_readme }}
|
linting_readme: ${{ !startsWith(github.ref, 'refs/pull/') || steps.filter.outputs.linting_readme }}
|
||||||
steps:
|
steps:
|
||||||
- uses: dorny/paths-filter@v3
|
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||||
id: filter
|
id: filter
|
||||||
# We only check on PRs
|
# We only check on PRs
|
||||||
if: startsWith(github.ref, 'refs/pull/')
|
if: startsWith(github.ref, 'refs/pull/')
|
||||||
@ -83,14 +83,14 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.linting == 'true' }}
|
if: ${{ needs.changes.outputs.linting == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.66.0
|
uses: dtolnay/rust-toolchain@c1678930c21fb233e4987c4ae12158f9125e5762 # 1.81.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
poetry-version: "1.3.2"
|
poetry-version: "2.1.1"
|
||||||
extras: "all"
|
extras: "all"
|
||||||
- run: poetry run scripts-dev/generate_sample_config.sh --check
|
- run: poetry run scripts-dev/generate_sample_config.sh --check
|
||||||
- run: poetry run scripts-dev/config-lint.sh
|
- run: poetry run scripts-dev/config-lint.sh
|
||||||
@ -101,8 +101,8 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.linting == 'true' }}
|
if: ${{ needs.changes.outputs.linting == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- run: "pip install 'click==8.1.1' 'GitPython>=3.1.20'"
|
- run: "pip install 'click==8.1.1' 'GitPython>=3.1.20'"
|
||||||
@ -111,8 +111,8 @@ jobs:
|
|||||||
check-lockfile:
|
check-lockfile:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- run: .ci/scripts/check_lockfile.py
|
- run: .ci/scripts/check_lockfile.py
|
||||||
@ -124,11 +124,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Poetry
|
- name: Setup Poetry
|
||||||
uses: matrix-org/setup-python-poetry@v1
|
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
|
poetry-version: "2.1.1"
|
||||||
install-project: "false"
|
install-project: "false"
|
||||||
|
|
||||||
- name: Run ruff check
|
- name: Run ruff check
|
||||||
@ -145,14 +146,14 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.66.0
|
uses: dtolnay/rust-toolchain@c1678930c21fb233e4987c4ae12158f9125e5762 # 1.81.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- name: Setup Poetry
|
- name: Setup Poetry
|
||||||
uses: matrix-org/setup-python-poetry@v1
|
uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
# We want to make use of type hints in optional dependencies too.
|
# We want to make use of type hints in optional dependencies too.
|
||||||
extras: all
|
extras: all
|
||||||
@ -161,11 +162,12 @@ jobs:
|
|||||||
# https://github.com/matrix-org/synapse/pull/15376#issuecomment-1498983775
|
# https://github.com/matrix-org/synapse/pull/15376#issuecomment-1498983775
|
||||||
# To make CI green, err towards caution and install the project.
|
# To make CI green, err towards caution and install the project.
|
||||||
install-project: "true"
|
install-project: "true"
|
||||||
|
poetry-version: "2.1.1"
|
||||||
|
|
||||||
# Cribbed from
|
# Cribbed from
|
||||||
# https://github.com/AustinScola/mypy-cache-github-action/blob/85ea4f2972abed39b33bd02c36e341b28ca59213/src/restore.ts#L10-L17
|
# https://github.com/AustinScola/mypy-cache-github-action/blob/85ea4f2972abed39b33bd02c36e341b28ca59213/src/restore.ts#L10-L17
|
||||||
- name: Restore/persist mypy's cache
|
- name: Restore/persist mypy's cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
@ -178,7 +180,7 @@ jobs:
|
|||||||
lint-crlf:
|
lint-crlf:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Check line endings
|
- name: Check line endings
|
||||||
run: scripts-dev/check_line_terminators.sh
|
run: scripts-dev/check_line_terminators.sh
|
||||||
|
|
||||||
@ -186,11 +188,11 @@ jobs:
|
|||||||
if: ${{ (github.base_ref == 'develop' || contains(github.base_ref, 'release-')) && github.actor != 'dependabot[bot]' }}
|
if: ${{ (github.base_ref == 'develop' || contains(github.base_ref, 'release-')) && github.actor != 'dependabot[bot]' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- run: "pip install 'towncrier>=18.6.0rc1'"
|
- run: "pip install 'towncrier>=18.6.0rc1'"
|
||||||
@ -204,15 +206,15 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.linting == 'true' }}
|
if: ${{ needs.changes.outputs.linting == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.66.0
|
uses: dtolnay/rust-toolchain@c1678930c21fb233e4987c4ae12158f9125e5762 # 1.81.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
poetry-version: "1.3.2"
|
poetry-version: "2.1.1"
|
||||||
extras: "all"
|
extras: "all"
|
||||||
- run: poetry run scripts-dev/check_pydantic_models.py
|
- run: poetry run scripts-dev/check_pydantic_models.py
|
||||||
|
|
||||||
@ -222,13 +224,13 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.rust == 'true' }}
|
if: ${{ needs.changes.outputs.rust == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.66.0
|
uses: dtolnay/rust-toolchain@0d72692bcfbf448b1e2afa01a67f71b455a9dcec # 1.86.0
|
||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- run: cargo clippy -- -D warnings
|
- run: cargo clippy -- -D warnings
|
||||||
|
|
||||||
@ -240,14 +242,14 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.rust == 'true' }}
|
if: ${{ needs.changes.outputs.rust == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: dtolnay/rust-toolchain@56f84321dbccf38fb67ce29ab63e4754056677e0 # master (rust 1.85.1)
|
||||||
with:
|
with:
|
||||||
toolchain: nightly-2022-12-01
|
toolchain: nightly-2025-04-23
|
||||||
components: clippy
|
components: clippy
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- run: cargo clippy --all-features -- -D warnings
|
- run: cargo clippy --all-features -- -D warnings
|
||||||
|
|
||||||
@ -257,15 +259,15 @@ jobs:
|
|||||||
if: ${{ needs.changes.outputs.rust == 'true' }}
|
if: ${{ needs.changes.outputs.rust == 'true' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: dtolnay/rust-toolchain@56f84321dbccf38fb67ce29ab63e4754056677e0 # master (rust 1.85.1)
|
||||||
with:
|
with:
|
||||||
# We use nightly so that it correctly groups together imports
|
# We use nightly so that it correctly groups together imports
|
||||||
toolchain: nightly-2022-12-01
|
toolchain: nightly-2025-04-23
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- run: cargo fmt --check
|
- run: cargo fmt --check
|
||||||
|
|
||||||
@ -276,8 +278,8 @@ jobs:
|
|||||||
needs: changes
|
needs: changes
|
||||||
if: ${{ needs.changes.outputs.linting_readme == 'true' }}
|
if: ${{ needs.changes.outputs.linting_readme == 'true' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- run: "pip install rstcheck"
|
- run: "pip install rstcheck"
|
||||||
@ -301,7 +303,7 @@ jobs:
|
|||||||
- lint-readme
|
- lint-readme
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: matrix-org/done-action@v3
|
- uses: matrix-org/done-action@3409aa904e8a2aaf2220f09bc954d3d0b0a2ee67 # v3
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|
||||||
@ -324,8 +326,8 @@ jobs:
|
|||||||
needs: linting-done
|
needs: linting-done
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- id: get-matrix
|
- id: get-matrix
|
||||||
@ -345,7 +347,7 @@ jobs:
|
|||||||
job: ${{ fromJson(needs.calculate-test-jobs.outputs.trial_test_matrix) }}
|
job: ${{ fromJson(needs.calculate-test-jobs.outputs.trial_test_matrix) }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: sudo apt-get -qq install xmlsec1
|
- run: sudo apt-get -qq install xmlsec1
|
||||||
- name: Set up PostgreSQL ${{ matrix.job.postgres-version }}
|
- name: Set up PostgreSQL ${{ matrix.job.postgres-version }}
|
||||||
if: ${{ matrix.job.postgres-version }}
|
if: ${{ matrix.job.postgres-version }}
|
||||||
@ -360,13 +362,13 @@ jobs:
|
|||||||
postgres:${{ matrix.job.postgres-version }}
|
postgres:${{ matrix.job.postgres-version }}
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.66.0
|
uses: dtolnay/rust-toolchain@c1678930c21fb233e4987c4ae12158f9125e5762 # 1.81.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.job.python-version }}
|
python-version: ${{ matrix.job.python-version }}
|
||||||
poetry-version: "1.3.2"
|
poetry-version: "2.1.1"
|
||||||
extras: ${{ matrix.job.extras }}
|
extras: ${{ matrix.job.extras }}
|
||||||
- name: Await PostgreSQL
|
- name: Await PostgreSQL
|
||||||
if: ${{ matrix.job.postgres-version }}
|
if: ${{ matrix.job.postgres-version }}
|
||||||
@ -399,11 +401,11 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.66.0
|
uses: dtolnay/rust-toolchain@c1678930c21fb233e4987c4ae12158f9125e5762 # 1.81.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
# There aren't wheels for some of the older deps, so we need to install
|
# There aren't wheels for some of the older deps, so we need to install
|
||||||
# their build dependencies
|
# their build dependencies
|
||||||
@ -412,7 +414,7 @@ jobs:
|
|||||||
sudo apt-get -qq install build-essential libffi-dev python3-dev \
|
sudo apt-get -qq install build-essential libffi-dev python3-dev \
|
||||||
libxml2-dev libxslt-dev xmlsec1 zlib1g-dev libjpeg-dev libwebp-dev
|
libxml2-dev libxslt-dev xmlsec1 zlib1g-dev libjpeg-dev libwebp-dev
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
|
|
||||||
@ -462,13 +464,13 @@ jobs:
|
|||||||
extras: ["all"]
|
extras: ["all"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
# Install libs necessary for PyPy to build binary wheels for dependencies
|
# Install libs necessary for PyPy to build binary wheels for dependencies
|
||||||
- run: sudo apt-get -qq install xmlsec1 libxml2-dev libxslt-dev
|
- run: sudo apt-get -qq install xmlsec1 libxml2-dev libxslt-dev
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
poetry-version: "1.3.2"
|
poetry-version: "2.1.1"
|
||||||
extras: ${{ matrix.extras }}
|
extras: ${{ matrix.extras }}
|
||||||
- run: poetry run trial --jobs=2 tests
|
- run: poetry run trial --jobs=2 tests
|
||||||
- name: Dump logs
|
- name: Dump logs
|
||||||
@ -512,13 +514,13 @@ jobs:
|
|||||||
job: ${{ fromJson(needs.calculate-test-jobs.outputs.sytest_test_matrix) }}
|
job: ${{ fromJson(needs.calculate-test-jobs.outputs.sytest_test_matrix) }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Prepare test blacklist
|
- name: Prepare test blacklist
|
||||||
run: cat sytest-blacklist .ci/worker-blacklist > synapse-blacklist-with-workers
|
run: cat sytest-blacklist .ci/worker-blacklist > synapse-blacklist-with-workers
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.66.0
|
uses: dtolnay/rust-toolchain@c1678930c21fb233e4987c4ae12158f9125e5762 # 1.81.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- name: Run SyTest
|
- name: Run SyTest
|
||||||
run: /bootstrap.sh synapse
|
run: /bootstrap.sh synapse
|
||||||
@ -527,7 +529,7 @@ jobs:
|
|||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
run: /sytest/scripts/tap_to_gha.pl /logs/results.tap
|
run: /sytest/scripts/tap_to_gha.pl /logs/results.tap
|
||||||
- name: Upload SyTest logs
|
- name: Upload SyTest logs
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: Sytest Logs - ${{ job.status }} - (${{ join(matrix.job.*, ', ') }})
|
name: Sytest Logs - ${{ job.status }} - (${{ join(matrix.job.*, ', ') }})
|
||||||
@ -557,11 +559,11 @@ jobs:
|
|||||||
--health-retries 5
|
--health-retries 5
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: sudo apt-get -qq install xmlsec1 postgresql-client
|
- run: sudo apt-get -qq install xmlsec1 postgresql-client
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
poetry-version: "1.3.2"
|
poetry-version: "2.1.1"
|
||||||
extras: "postgres"
|
extras: "postgres"
|
||||||
- run: .ci/scripts/test_export_data_command.sh
|
- run: .ci/scripts/test_export_data_command.sh
|
||||||
env:
|
env:
|
||||||
@ -601,7 +603,7 @@ jobs:
|
|||||||
--health-retries 5
|
--health-retries 5
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Add PostgreSQL apt repository
|
- name: Add PostgreSQL apt repository
|
||||||
# We need a version of pg_dump that can handle the version of
|
# We need a version of pg_dump that can handle the version of
|
||||||
# PostgreSQL being tested against. The Ubuntu package repository lags
|
# PostgreSQL being tested against. The Ubuntu package repository lags
|
||||||
@ -612,10 +614,10 @@ jobs:
|
|||||||
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
- run: sudo apt-get -qq install xmlsec1 postgresql-client
|
- run: sudo apt-get -qq install xmlsec1 postgresql-client
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
poetry-version: "1.3.2"
|
poetry-version: "2.1.1"
|
||||||
extras: "postgres"
|
extras: "postgres"
|
||||||
- run: .ci/scripts/test_synapse_port_db.sh
|
- run: .ci/scripts/test_synapse_port_db.sh
|
||||||
id: run_tester_script
|
id: run_tester_script
|
||||||
@ -625,7 +627,7 @@ jobs:
|
|||||||
PGPASSWORD: postgres
|
PGPASSWORD: postgres
|
||||||
PGDATABASE: postgres
|
PGDATABASE: postgres
|
||||||
- name: "Upload schema differences"
|
- name: "Upload schema differences"
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
if: ${{ failure() && !cancelled() && steps.run_tester_script.outcome == 'failure' }}
|
if: ${{ failure() && !cancelled() && steps.run_tester_script.outcome == 'failure' }}
|
||||||
with:
|
with:
|
||||||
name: Schema dumps
|
name: Schema dumps
|
||||||
@ -655,19 +657,19 @@ jobs:
|
|||||||
database: Postgres
|
database: Postgres
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Run actions/checkout@v4 for synapse
|
- name: Checkout synapse codebase
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
path: synapse
|
path: synapse
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.66.0
|
uses: dtolnay/rust-toolchain@c1678930c21fb233e4987c4ae12158f9125e5762 # 1.81.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- name: Prepare Complement's Prerequisites
|
- name: Prepare Complement's Prerequisites
|
||||||
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
cache-dependency-path: complement/go.sum
|
cache-dependency-path: complement/go.sum
|
||||||
go-version-file: complement/go.mod
|
go-version-file: complement/go.mod
|
||||||
@ -690,11 +692,11 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@1.66.0
|
uses: dtolnay/rust-toolchain@c1678930c21fb233e4987c4ae12158f9125e5762 # 1.81.0
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- run: cargo test
|
- run: cargo test
|
||||||
|
|
||||||
@ -708,13 +710,13 @@ jobs:
|
|||||||
- changes
|
- changes
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: dtolnay/rust-toolchain@56f84321dbccf38fb67ce29ab63e4754056677e0 # master (rust 1.85.1)
|
||||||
with:
|
with:
|
||||||
toolchain: nightly-2022-12-01
|
toolchain: nightly-2022-12-01
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- run: cargo bench --no-run
|
- run: cargo bench --no-run
|
||||||
|
|
||||||
@ -733,7 +735,7 @@ jobs:
|
|||||||
- linting-done
|
- linting-done
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: matrix-org/done-action@v3
|
- uses: matrix-org/done-action@3409aa904e8a2aaf2220f09bc954d3d0b0a2ee67 # v3
|
||||||
with:
|
with:
|
||||||
needs: ${{ toJSON(needs) }}
|
needs: ${{ toJSON(needs) }}
|
||||||
|
|
||||||
|
2
.github/workflows/triage-incoming.yml
vendored
2
.github/workflows/triage-incoming.yml
vendored
@ -6,7 +6,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
triage:
|
triage:
|
||||||
uses: matrix-org/backend-meta/.github/workflows/triage-incoming.yml@v2
|
uses: matrix-org/backend-meta/.github/workflows/triage-incoming.yml@18beaf3c8e536108bd04d18e6c3dc40ba3931e28 # v2.0.3
|
||||||
with:
|
with:
|
||||||
project_id: 'PVT_kwDOAIB0Bs4AFDdZ'
|
project_id: 'PVT_kwDOAIB0Bs4AFDdZ'
|
||||||
content_id: ${{ github.event.issue.node_id }}
|
content_id: ${{ github.event.issue.node_id }}
|
||||||
|
2
.github/workflows/triage_labelled.yml
vendored
2
.github/workflows/triage_labelled.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
|||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'X-Needs-Info')
|
contains(github.event.issue.labels.*.name, 'X-Needs-Info')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@main
|
- uses: actions/add-to-project@5b1a254a3546aef88e0a7724a77a623fa2e47c36 # main (v1.0.2 + 10 commits)
|
||||||
id: add_project
|
id: add_project
|
||||||
with:
|
with:
|
||||||
project-url: "https://github.com/orgs/matrix-org/projects/67"
|
project-url: "https://github.com/orgs/matrix-org/projects/67"
|
||||||
|
36
.github/workflows/twisted_trunk.yml
vendored
36
.github/workflows/twisted_trunk.yml
vendored
@ -40,16 +40,17 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@fcf085fcb4b4b8f63f96906cd713eb52181b5ea4 # stable (rust 1.85.1)
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
extras: "all"
|
extras: "all"
|
||||||
|
poetry-version: "2.1.1"
|
||||||
- run: |
|
- run: |
|
||||||
poetry remove twisted
|
poetry remove twisted
|
||||||
poetry add --extras tls git+https://github.com/twisted/twisted.git#${{ inputs.twisted_ref || 'trunk' }}
|
poetry add --extras tls git+https://github.com/twisted/twisted.git#${{ inputs.twisted_ref || 'trunk' }}
|
||||||
@ -64,17 +65,18 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: sudo apt-get -qq install xmlsec1
|
- run: sudo apt-get -qq install xmlsec1
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@fcf085fcb4b4b8f63f96906cd713eb52181b5ea4 # stable (rust 1.85.1)
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- uses: matrix-org/setup-python-poetry@v1
|
- uses: matrix-org/setup-python-poetry@5bbf6603c5c930615ec8a29f1b5d7d258d905aa4 # v2.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
extras: "all test"
|
extras: "all test"
|
||||||
|
poetry-version: "2.1.1"
|
||||||
- run: |
|
- run: |
|
||||||
poetry remove twisted
|
poetry remove twisted
|
||||||
poetry add --extras tls git+https://github.com/twisted/twisted.git#trunk
|
poetry add --extras tls git+https://github.com/twisted/twisted.git#trunk
|
||||||
@ -108,11 +110,11 @@ jobs:
|
|||||||
- ${{ github.workspace }}:/src
|
- ${{ github.workspace }}:/src
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@fcf085fcb4b4b8f63f96906cd713eb52181b5ea4 # stable (rust 1.85.1)
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||||
|
|
||||||
- name: Patch dependencies
|
- name: Patch dependencies
|
||||||
# Note: The poetry commands want to create a virtualenv in /src/.venv/,
|
# Note: The poetry commands want to create a virtualenv in /src/.venv/,
|
||||||
@ -136,7 +138,7 @@ jobs:
|
|||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
run: /sytest/scripts/tap_to_gha.pl /logs/results.tap
|
run: /sytest/scripts/tap_to_gha.pl /logs/results.tap
|
||||||
- name: Upload SyTest logs
|
- name: Upload SyTest logs
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: Sytest Logs - ${{ job.status }} - (${{ join(matrix.*, ', ') }})
|
name: Sytest Logs - ${{ job.status }} - (${{ join(matrix.*, ', ') }})
|
||||||
@ -164,14 +166,14 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Run actions/checkout@v4 for synapse
|
- name: Run actions/checkout@v4 for synapse
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
path: synapse
|
path: synapse
|
||||||
|
|
||||||
- name: Prepare Complement's Prerequisites
|
- name: Prepare Complement's Prerequisites
|
||||||
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
run: synapse/.ci/scripts/setup_complement_prerequisites.sh
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||||
with:
|
with:
|
||||||
cache-dependency-path: complement/go.sum
|
cache-dependency-path: complement/go.sum
|
||||||
go-version-file: complement/go.mod
|
go-version-file: complement/go.mod
|
||||||
@ -181,11 +183,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -x
|
set -x
|
||||||
DEBIAN_FRONTEND=noninteractive sudo apt-get install -yqq python3 pipx
|
DEBIAN_FRONTEND=noninteractive sudo apt-get install -yqq python3 pipx
|
||||||
pipx install poetry==1.3.2
|
pipx install poetry==2.1.1
|
||||||
|
|
||||||
poetry remove -n twisted
|
poetry remove -n twisted
|
||||||
poetry add -n --extras tls git+https://github.com/twisted/twisted.git#trunk
|
poetry add -n --extras tls git+https://github.com/twisted/twisted.git#trunk
|
||||||
poetry lock --no-update
|
poetry lock
|
||||||
working-directory: synapse
|
working-directory: synapse
|
||||||
|
|
||||||
- run: |
|
- run: |
|
||||||
@ -206,7 +208,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2
|
- uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
382
CHANGES.md
382
CHANGES.md
@ -1,9 +1,389 @@
|
|||||||
# Synapse 1.126.0rc1 (2025-03-04)
|
# Synapse 1.133.0rc1 (2025-06-24)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add support for the [MSC4260 user report API](https://github.com/matrix-org/matrix-spec-proposals/pull/4260). ([\#18120](https://github.com/element-hq/synapse/issues/18120))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Fix an issue where, during state resolution for v11 rooms, Synapse would incorrectly calculate the power level of the creator when there was no power levels event in the room. ([\#18534](https://github.com/element-hq/synapse/issues/18534), [\#18547](https://github.com/element-hq/synapse/issues/18547))
|
||||||
|
- Fix long-standing bug where sliding sync did not honour the `room_id_to_include` config option. ([\#18535](https://github.com/element-hq/synapse/issues/18535))
|
||||||
|
- Fix an issue where "Lock timeout is getting excessive" warnings would be logged even when the lock timeout was <10 minutes. ([\#18543](https://github.com/element-hq/synapse/issues/18543))
|
||||||
|
- Fix an issue where Synapse could calculate the wrong power level for the creator of the room if there was no power levels event. ([\#18545](https://github.com/element-hq/synapse/issues/18545))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Generate config documentation from JSON Schema file. ([\#18528](https://github.com/element-hq/synapse/issues/18528))
|
||||||
|
- Fix typo in user type documentation. ([\#18568](https://github.com/element-hq/synapse/issues/18568))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Increase performance of introspecting access tokens when using delegated auth. ([\#18357](https://github.com/element-hq/synapse/issues/18357), [\#18561](https://github.com/element-hq/synapse/issues/18561))
|
||||||
|
- Log user deactivations. ([\#18541](https://github.com/element-hq/synapse/issues/18541))
|
||||||
|
- Enable [`flake8-logging`](https://docs.astral.sh/ruff/rules/#flake8-logging-log) and [`flake8-logging-format`](https://docs.astral.sh/ruff/rules/#flake8-logging-format-g) rules in Ruff and fix related issues throughout the codebase. ([\#18542](https://github.com/element-hq/synapse/issues/18542))
|
||||||
|
- Clean up old, unused rows from the `device_federation_inbox` table. ([\#18546](https://github.com/element-hq/synapse/issues/18546))
|
||||||
|
- Run config schema CI on develop and release branches. ([\#18551](https://github.com/element-hq/synapse/issues/18551))
|
||||||
|
- Add support for Twisted `25.5.0`+ releases. ([\#18577](https://github.com/element-hq/synapse/issues/18577))
|
||||||
|
- Update PyO3 to version 0.25. ([\#18578](https://github.com/element-hq/synapse/issues/18578))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump actions/setup-python from 5.5.0 to 5.6.0. ([\#18555](https://github.com/element-hq/synapse/issues/18555))
|
||||||
|
* Bump base64 from 0.21.7 to 0.22.1. ([\#18559](https://github.com/element-hq/synapse/issues/18559))
|
||||||
|
* Bump dawidd6/action-download-artifact from 9 to 11. ([\#18556](https://github.com/element-hq/synapse/issues/18556))
|
||||||
|
* Bump headers from 0.4.0 to 0.4.1. ([\#18529](https://github.com/element-hq/synapse/issues/18529))
|
||||||
|
* Bump requests from 2.32.2 to 2.32.4. ([\#18533](https://github.com/element-hq/synapse/issues/18533))
|
||||||
|
* Bump types-requests from 2.32.0.20250328 to 2.32.4.20250611. ([\#18558](https://github.com/element-hq/synapse/issues/18558))
|
||||||
|
|
||||||
|
# Synapse 1.132.0 (2025-06-17)
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Improvements to generate config documentation from JSON Schema file. ([\#18522](https://github.com/element-hq/synapse/issues/18522))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.132.0rc1 (2025-06-10)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add support for [MSC4155](https://github.com/matrix-org/matrix-spec-proposals/pull/4155) Invite Filtering. ([\#18288](https://github.com/element-hq/synapse/issues/18288))
|
||||||
|
- Add experimental `user_may_send_state_event` module API callback. ([\#18455](https://github.com/element-hq/synapse/issues/18455))
|
||||||
|
- Add experimental `get_media_config_for_user` and `is_user_allowed_to_upload_media_of_size` module API callbacks that allow overriding of media repository maximum upload size. ([\#18457](https://github.com/element-hq/synapse/issues/18457))
|
||||||
|
- Add experimental `get_ratelimit_override_for_user` module API callback that allows overriding of per-user ratelimits. ([\#18458](https://github.com/element-hq/synapse/issues/18458))
|
||||||
|
- Pass `room_config` argument to `user_may_create_room` spam checker module callback. ([\#18486](https://github.com/element-hq/synapse/issues/18486))
|
||||||
|
- Support configuration of default and extra user types. ([\#18456](https://github.com/element-hq/synapse/issues/18456))
|
||||||
|
- Successful requests to `/_matrix/app/v1/ping` will now force Synapse to reattempt delivering transactions to appservices. ([\#18521](https://github.com/element-hq/synapse/issues/18521))
|
||||||
|
- Support the import of the `RatelimitOverride` type from `synapse.module_api` in modules and rename `messages_per_second` to `per_second`. ([\#18513](https://github.com/element-hq/synapse/issues/18513))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Remove destinations from sending if not whitelisted. ([\#18484](https://github.com/element-hq/synapse/issues/18484))
|
||||||
|
- Fixed room summary API incorrectly returning that a room is private in the room summary response when the join rule is omitted by the remote server. Contributed by @nexy7574. ([\#18493](https://github.com/element-hq/synapse/issues/18493))
|
||||||
|
- Prevent users from adding themselves to their own user ignore list. ([\#18508](https://github.com/element-hq/synapse/issues/18508))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Generate config documentation from JSON Schema file. ([\#17892](https://github.com/element-hq/synapse/issues/17892))
|
||||||
|
- Mention `CAP_NET_BIND_SERVICE` as an alternative to running Synapse as root in order to bind to a privileged port. ([\#18408](https://github.com/element-hq/synapse/issues/18408))
|
||||||
|
- Surface hidden Admin API documentation regarding fetching of scheduled tasks. ([\#18516](https://github.com/element-hq/synapse/issues/18516))
|
||||||
|
- Mark the new module APIs in this release as experimental. ([\#18536](https://github.com/element-hq/synapse/issues/18536))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Mark dehydrated devices in the [List All User Devices Admin API](https://element-hq.github.io/synapse/latest/admin_api/user_admin_api.html#list-all-devices). ([\#18252](https://github.com/element-hq/synapse/issues/18252))
|
||||||
|
- Reduce disk wastage by cleaning up `received_transactions` older than 1 day, rather than 30 days. ([\#18310](https://github.com/element-hq/synapse/issues/18310))
|
||||||
|
- Distinguish all vs local events being persisted in the "Event Send Time Quantiles" graph (Grafana). ([\#18510](https://github.com/element-hq/synapse/issues/18510))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.131.0 (2025-06-03)
|
||||||
|
|
||||||
|
No significant changes since 1.131.0rc1.
|
||||||
|
|
||||||
|
# Synapse 1.131.0rc1 (2025-05-28)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add `msc4263_limit_key_queries_to_users_who_share_rooms` config option as per [MSC4263](https://github.com/matrix-org/matrix-spec-proposals/pull/4263). ([\#18180](https://github.com/element-hq/synapse/issues/18180))
|
||||||
|
- Add option to allow registrations that begin with `_`. Contributed by `_` (@hex5f). ([\#18262](https://github.com/element-hq/synapse/issues/18262))
|
||||||
|
- Include room ID in response to the [Room Deletion Status Admin API](https://element-hq.github.io/synapse/latest/admin_api/rooms.html#status-of-deleting-rooms). ([\#18318](https://github.com/element-hq/synapse/issues/18318))
|
||||||
|
- Add support for calling Policy Servers ([MSC4284](https://github.com/matrix-org/matrix-spec-proposals/pull/4284)) to mark events as spam. ([\#18387](https://github.com/element-hq/synapse/issues/18387))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Prevent race-condition in `_maybe_retry_device_resync` entrance. ([\#18391](https://github.com/element-hq/synapse/issues/18391))
|
||||||
|
- Fix the `tests.handlers.test_worker_lock.WorkerLockTestCase.test_lock_contention` test which could spuriously time out on RISC-V architectures due to performance differences. ([\#18430](https://github.com/element-hq/synapse/issues/18430))
|
||||||
|
- Fix admin redaction endpoint not redacting encrypted messages. ([\#18434](https://github.com/element-hq/synapse/issues/18434))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Update `room_list_publication_rules` docs to consider defaults that changed in v1.126.0. Contributed by @HarHarLinks. ([\#18286](https://github.com/element-hq/synapse/issues/18286))
|
||||||
|
- Add advice for upgrading between major PostgreSQL versions to the database documentation. ([\#18445](https://github.com/element-hq/synapse/issues/18445))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Fix a memory leak in `_NotifierUserStream`. ([\#18380](https://github.com/element-hq/synapse/issues/18380))
|
||||||
|
- Fix a couple type annotations in the `RootConfig`/`Config`. ([\#18409](https://github.com/element-hq/synapse/issues/18409))
|
||||||
|
- Explicitly enable PyPy builds in `cibuildwheel`s config to avoid it being disabled on a future upgrade to `cibuildwheel` v3. ([\#18417](https://github.com/element-hq/synapse/issues/18417))
|
||||||
|
- Update the PR review template to remove an erroneous line break from the final bullet point. ([\#18419](https://github.com/element-hq/synapse/issues/18419))
|
||||||
|
- Explain why we `flush_buffer()` for Python `print(...)` output. ([\#18420](https://github.com/element-hq/synapse/issues/18420))
|
||||||
|
- Add lint to ensure we don't add a `CREATE/DROP INDEX` in a schema delta. ([\#18440](https://github.com/element-hq/synapse/issues/18440))
|
||||||
|
- Allow checking only for the existence of a field in an SSO provider's response, rather than requiring the value(s) to check. ([\#18454](https://github.com/element-hq/synapse/issues/18454))
|
||||||
|
- Add unit tests for homeserver usage statistics. ([\#18463](https://github.com/element-hq/synapse/issues/18463))
|
||||||
|
- Don't move invited users to new room when shutting down room. ([\#18471](https://github.com/element-hq/synapse/issues/18471))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump actions/setup-python from 5.5.0 to 5.6.0. ([\#18398](https://github.com/element-hq/synapse/issues/18398))
|
||||||
|
* Bump authlib from 1.5.1 to 1.5.2. ([\#18452](https://github.com/element-hq/synapse/issues/18452))
|
||||||
|
* Bump docker/build-push-action from 6.15.0 to 6.17.0. ([\#18397](https://github.com/element-hq/synapse/issues/18397), [\#18449](https://github.com/element-hq/synapse/issues/18449))
|
||||||
|
* Bump lxml from 5.3.0 to 5.4.0. ([\#18480](https://github.com/element-hq/synapse/issues/18480))
|
||||||
|
* Bump mypy-zope from 1.0.9 to 1.0.11. ([\#18428](https://github.com/element-hq/synapse/issues/18428))
|
||||||
|
* Bump pyo3 from 0.23.5 to 0.24.2. ([\#18460](https://github.com/element-hq/synapse/issues/18460))
|
||||||
|
* Bump pyo3-log from 0.12.3 to 0.12.4. ([\#18453](https://github.com/element-hq/synapse/issues/18453))
|
||||||
|
* Bump pyopenssl from 25.0.0 to 25.1.0. ([\#18450](https://github.com/element-hq/synapse/issues/18450))
|
||||||
|
* Bump ruff from 0.7.3 to 0.11.11. ([\#18451](https://github.com/element-hq/synapse/issues/18451), [\#18482](https://github.com/element-hq/synapse/issues/18482))
|
||||||
|
* Bump tornado from 6.4.2 to 6.5.0. ([\#18459](https://github.com/element-hq/synapse/issues/18459))
|
||||||
|
* Bump setuptools from 72.1.0 to 78.1.1. ([\#18461](https://github.com/element-hq/synapse/issues/18461))
|
||||||
|
* Bump types-jsonschema from 4.23.0.20241208 to 4.23.0.20250516. ([\#18481](https://github.com/element-hq/synapse/issues/18481))
|
||||||
|
* Bump types-requests from 2.32.0.20241016 to 2.32.0.20250328. ([\#18427](https://github.com/element-hq/synapse/issues/18427))
|
||||||
|
|
||||||
|
# Synapse 1.130.0 (2025-05-20)
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Fix startup being blocked on creating a new index that was introduced in v1.130.0rc1. ([\#18439](https://github.com/element-hq/synapse/issues/18439))
|
||||||
|
- Fix the ordering of local messages in rooms that were affected by [GHSA-v56r-hwv5-mxg6](https://github.com/advisories/GHSA-v56r-hwv5-mxg6). ([\#18447](https://github.com/element-hq/synapse/issues/18447))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.130.0rc1 (2025-05-13)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add an Admin API endpoint `GET /_synapse/admin/v1/scheduled_tasks` to fetch scheduled tasks. ([\#18214](https://github.com/element-hq/synapse/issues/18214))
|
||||||
|
- Add config option `user_directory.exclude_remote_users` which, when enabled, excludes remote users from user directory search results. ([\#18300](https://github.com/element-hq/synapse/issues/18300))
|
||||||
|
- Add support for handling `GET /devices/` on workers. ([\#18355](https://github.com/element-hq/synapse/issues/18355))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Fix a longstanding bug where Synapse would immediately retry a failing push endpoint when a new event is received, ignoring any backoff timers. ([\#18363](https://github.com/element-hq/synapse/issues/18363))
|
||||||
|
- Pass leave from remote invite rejection down Sliding Sync. ([\#18375](https://github.com/element-hq/synapse/issues/18375))
|
||||||
|
|
||||||
|
### Updates to the Docker image
|
||||||
|
|
||||||
|
- In `configure_workers_and_start.py`, use the same absolute path of Python in the interpreter shebang, and invoke child Python processes with `sys.executable`. ([\#18291](https://github.com/element-hq/synapse/issues/18291))
|
||||||
|
- Optimize the build of the workers image. ([\#18292](https://github.com/element-hq/synapse/issues/18292))
|
||||||
|
- In `start_for_complement.sh`, replace some external program calls with shell builtins. ([\#18293](https://github.com/element-hq/synapse/issues/18293))
|
||||||
|
- When generating container scripts from templates, don't add a leading newline so that their shebangs may be handled correctly. ([\#18295](https://github.com/element-hq/synapse/issues/18295))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Improve formatting of the README file. ([\#18218](https://github.com/element-hq/synapse/issues/18218))
|
||||||
|
- Add documentation for configuring [Pocket ID](https://github.com/pocket-id/pocket-id) as an OIDC provider. ([\#18237](https://github.com/element-hq/synapse/issues/18237))
|
||||||
|
- Fix typo in docs about the `push` config option. Contributed by @HarHarLinks. ([\#18320](https://github.com/element-hq/synapse/issues/18320))
|
||||||
|
- Add `/_matrix/federation/v1/version` to list of federation endpoints that can be handled by workers. ([\#18377](https://github.com/element-hq/synapse/issues/18377))
|
||||||
|
- Add an Admin API endpoint `GET /_synapse/admin/v1/scheduled_tasks` to fetch scheduled tasks. ([\#18384](https://github.com/element-hq/synapse/issues/18384))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Return specific error code when adding an email address / phone number to account is not supported ([MSC4178](https://github.com/matrix-org/matrix-spec-proposals/pull/4178)). ([\#17578](https://github.com/element-hq/synapse/issues/17578))
|
||||||
|
- Stop auto-provisionning missing users & devices when delegating auth to Matrix Authentication Service. Requires MAS 0.13.0 or later. ([\#18181](https://github.com/element-hq/synapse/issues/18181))
|
||||||
|
- Apply file hashing and existing quarantines to media downloaded for URL previews. ([\#18297](https://github.com/element-hq/synapse/issues/18297))
|
||||||
|
- Allow a few admin APIs used by matrix-authentication-service to run on workers. ([\#18313](https://github.com/element-hq/synapse/issues/18313))
|
||||||
|
- Apply `should_drop_federated_event` to federation invites. ([\#18330](https://github.com/element-hq/synapse/issues/18330))
|
||||||
|
- Allow `/rooms/` admin API to be run on workers. ([\#18360](https://github.com/element-hq/synapse/issues/18360))
|
||||||
|
- Minor performance improvements to the notifier. ([\#18367](https://github.com/element-hq/synapse/issues/18367))
|
||||||
|
- Slight performance increase when using the ratelimiter. ([\#18369](https://github.com/element-hq/synapse/issues/18369))
|
||||||
|
- Don't validate the `at_hash` (access token hash) field in OIDC ID Tokens if we don't end up actually using the OIDC Access Token. ([\#18374](https://github.com/element-hq/synapse/issues/18374), [\#18385](https://github.com/element-hq/synapse/issues/18385))
|
||||||
|
- Fixed test failures when using authlib 1.5.2. ([\#18390](https://github.com/element-hq/synapse/issues/18390))
|
||||||
|
- Refactor [MSC4186](https://github.com/matrix-org/matrix-spec-proposals/pull/4186) Simplified Sliding Sync room list tests to cover both new and fallback logic paths. ([\#18399](https://github.com/element-hq/synapse/issues/18399))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump actions/add-to-project from 280af8ae1f83a494cfad2cb10f02f6d13529caa9 to 5b1a254a3546aef88e0a7724a77a623fa2e47c36. ([\#18365](https://github.com/element-hq/synapse/issues/18365))
|
||||||
|
* Bump actions/download-artifact from 4.2.1 to 4.3.0. ([\#18364](https://github.com/element-hq/synapse/issues/18364))
|
||||||
|
* Bump actions/setup-go from 5.4.0 to 5.5.0. ([\#18426](https://github.com/element-hq/synapse/issues/18426))
|
||||||
|
* Bump anyhow from 1.0.97 to 1.0.98. ([\#18336](https://github.com/element-hq/synapse/issues/18336))
|
||||||
|
* Bump packaging from 24.2 to 25.0. ([\#18393](https://github.com/element-hq/synapse/issues/18393))
|
||||||
|
* Bump pillow from 11.1.0 to 11.2.1. ([\#18429](https://github.com/element-hq/synapse/issues/18429))
|
||||||
|
* Bump pydantic from 2.10.3 to 2.11.4. ([\#18394](https://github.com/element-hq/synapse/issues/18394))
|
||||||
|
* Bump pyo3-log from 0.12.2 to 0.12.3. ([\#18317](https://github.com/element-hq/synapse/issues/18317))
|
||||||
|
* Bump pyopenssl from 24.3.0 to 25.0.0. ([\#18315](https://github.com/element-hq/synapse/issues/18315))
|
||||||
|
* Bump sha2 from 0.10.8 to 0.10.9. ([\#18395](https://github.com/element-hq/synapse/issues/18395))
|
||||||
|
* Bump sigstore/cosign-installer from 3.8.1 to 3.8.2. ([\#18366](https://github.com/element-hq/synapse/issues/18366))
|
||||||
|
* Bump softprops/action-gh-release from 1 to 2. ([\#18264](https://github.com/element-hq/synapse/issues/18264))
|
||||||
|
* Bump stefanzweifel/git-auto-commit-action from 5.1.0 to 5.2.0. ([\#18354](https://github.com/element-hq/synapse/issues/18354))
|
||||||
|
* Bump txredisapi from 1.4.10 to 1.4.11. ([\#18392](https://github.com/element-hq/synapse/issues/18392))
|
||||||
|
* Bump types-jsonschema from 4.23.0.20240813 to 4.23.0.20241208. ([\#18305](https://github.com/element-hq/synapse/issues/18305))
|
||||||
|
* Bump types-psycopg2 from 2.9.21.20250121 to 2.9.21.20250318. ([\#18316](https://github.com/element-hq/synapse/issues/18316))
|
||||||
|
|
||||||
|
# Synapse 1.129.0 (2025-05-06)
|
||||||
|
|
||||||
|
No significant changes since 1.129.0rc2.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.129.0rc2 (2025-04-30)
|
||||||
|
|
||||||
|
Synapse 1.129.0rc1 was never formally released due to regressions discovered during the release process. 1.129.0rc2 fixes those regressions by reverting the affected PRs.
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Revert the slow background update introduced by [\#18068](https://github.com/element-hq/synapse/issues/18068) in v1.128.0. ([\#18372](https://github.com/element-hq/synapse/issues/18372))
|
||||||
|
- Revert "Add total event, unencrypted message, and e2ee event counts to stats reporting", added in v1.129.0rc1. ([\#18373](https://github.com/element-hq/synapse/issues/18373))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.129.0rc1 (2025-04-15)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add `passthrough_authorization_parameters` in OIDC configuration to allow passing parameters to the authorization grant URL. ([\#18232](https://github.com/element-hq/synapse/issues/18232))
|
||||||
|
- Add `total_event_count`, `total_message_count`, and `total_e2ee_event_count` fields to the homeserver usage statistics. ([\#18260](https://github.com/element-hq/synapse/issues/18260))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Fix `force_tracing_for_users` config when using delegated auth. ([\#18334](https://github.com/element-hq/synapse/issues/18334))
|
||||||
|
- Fix the token introspection cache logging access tokens when MAS integration is in use. ([\#18335](https://github.com/element-hq/synapse/issues/18335))
|
||||||
|
- Stop caching introspection failures when delegating auth to MAS. ([\#18339](https://github.com/element-hq/synapse/issues/18339))
|
||||||
|
- Fix `ExternalIDReuse` exception after migrating to MAS on workers with a high traffic. ([\#18342](https://github.com/element-hq/synapse/issues/18342))
|
||||||
|
- Fix minor performance regression caused by tracking of room participation. Regressed in v1.128.0. ([\#18345](https://github.com/element-hq/synapse/issues/18345))
|
||||||
|
|
||||||
|
### Updates to the Docker image
|
||||||
|
|
||||||
|
- Optimize the build of the complement-synapse image. ([\#18294](https://github.com/element-hq/synapse/issues/18294))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Disable statement timeout during room purge. ([\#18133](https://github.com/element-hq/synapse/issues/18133))
|
||||||
|
- Add cache to storage functions used to auth requests when using delegated auth. ([\#18337](https://github.com/element-hq/synapse/issues/18337))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.128.0 (2025-04-08)
|
||||||
|
|
||||||
|
No significant changes since 1.128.0rc1.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.128.0rc1 (2025-04-01)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add an access token introspection cache to make Matrix Authentication Service integration ([MSC3861](https://github.com/matrix-org/matrix-doc/pull/3861)) more efficient. ([\#18231](https://github.com/element-hq/synapse/issues/18231))
|
||||||
|
- Add background job to clear unreferenced state groups. ([\#18254](https://github.com/element-hq/synapse/issues/18254))
|
||||||
|
- Hashes of media files are now tracked by Synapse. Media quarantines will now apply to all files with the same hash. ([\#18277](https://github.com/element-hq/synapse/issues/18277), [\#18302](https://github.com/element-hq/synapse/issues/18302), [\#18296](https://github.com/element-hq/synapse/issues/18296))
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Add index to sliding sync ([MSC4186](https://github.com/matrix-org/matrix-doc/pull/4186)) membership snapshot table, to fix a performance issue. ([\#18074](https://github.com/element-hq/synapse/issues/18074))
|
||||||
|
|
||||||
|
### Updates to the Docker image
|
||||||
|
|
||||||
|
- Specify the architecture of installed packages via an APT config option, which is more reliable than appending package names with `:{arch}`. ([\#18271](https://github.com/element-hq/synapse/issues/18271))
|
||||||
|
- Always specify base image debian versions with a build argument. ([\#18272](https://github.com/element-hq/synapse/issues/18272))
|
||||||
|
- Allow passing arguments to `start_for_complement.sh` (to be sent to `configure_workers_and_start.py`). ([\#18273](https://github.com/element-hq/synapse/issues/18273))
|
||||||
|
- Make some improvements to the `prefix-log` script in the workers image. ([\#18274](https://github.com/element-hq/synapse/issues/18274))
|
||||||
|
- Use `uv pip` to install `supervisor` in the worker image. ([\#18275](https://github.com/element-hq/synapse/issues/18275))
|
||||||
|
- Avoid needing to download & use `rsync` in a build layer. ([\#18287](https://github.com/element-hq/synapse/issues/18287))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Fix how to obtain access token and change naming from riot to element ([\#18225](https://github.com/element-hq/synapse/issues/18225))
|
||||||
|
- Correct a small typo in the SSO mapping providers documentation. ([\#18276](https://github.com/element-hq/synapse/issues/18276))
|
||||||
|
- Add docs for how to clear out the Poetry wheel cache. ([\#18283](https://github.com/element-hq/synapse/issues/18283))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Add a column `participant` to `room_memberships` table. ([\#18068](https://github.com/element-hq/synapse/issues/18068))
|
||||||
|
- Update Poetry to 2.1.1, including updating the lock file version. ([\#18251](https://github.com/element-hq/synapse/issues/18251))
|
||||||
|
- Pin GitHub Actions dependencies by commit hash. ([\#18255](https://github.com/element-hq/synapse/issues/18255))
|
||||||
|
- Add DB delta to remove the old state group deletion job. ([\#18284](https://github.com/element-hq/synapse/issues/18284))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump actions/add-to-project from f5473ace9aeee8b97717b281e26980aa5097023f to 280af8ae1f83a494cfad2cb10f02f6d13529caa9. ([\#18303](https://github.com/element-hq/synapse/issues/18303))
|
||||||
|
* Bump actions/cache from 4.2.2 to 4.2.3. ([\#18266](https://github.com/element-hq/synapse/issues/18266))
|
||||||
|
* Bump actions/download-artifact from 4.2.0 to 4.2.1. ([\#18268](https://github.com/element-hq/synapse/issues/18268))
|
||||||
|
* Bump actions/setup-python from 5.4.0 to 5.5.0. ([\#18298](https://github.com/element-hq/synapse/issues/18298))
|
||||||
|
* Bump actions/upload-artifact from 4.6.1 to 4.6.2. ([\#18304](https://github.com/element-hq/synapse/issues/18304))
|
||||||
|
* Bump authlib from 1.4.1 to 1.5.1. ([\#18306](https://github.com/element-hq/synapse/issues/18306))
|
||||||
|
* Bump dawidd6/action-download-artifact from 8 to 9. ([\#18204](https://github.com/element-hq/synapse/issues/18204))
|
||||||
|
* Bump jinja2 from 3.1.5 to 3.1.6. ([\#18223](https://github.com/element-hq/synapse/issues/18223))
|
||||||
|
* Bump log from 0.4.26 to 0.4.27. ([\#18267](https://github.com/element-hq/synapse/issues/18267))
|
||||||
|
* Bump phonenumbers from 8.13.50 to 9.0.2. ([\#18299](https://github.com/element-hq/synapse/issues/18299))
|
||||||
|
* Bump pygithub from 2.5.0 to 2.6.1. ([\#18243](https://github.com/element-hq/synapse/issues/18243))
|
||||||
|
* Bump pyo3-log from 0.12.1 to 0.12.2. ([\#18269](https://github.com/element-hq/synapse/issues/18269))
|
||||||
|
|
||||||
|
# Synapse 1.127.1 (2025-03-26)
|
||||||
|
|
||||||
|
## Security
|
||||||
|
- Fix [CVE-2025-30355](https://www.cve.org/CVERecord?id=CVE-2025-30355) / [GHSA-v56r-hwv5-mxg6](https://github.com/element-hq/synapse/security/advisories/GHSA-v56r-hwv5-mxg6). **High severity vulnerability affecting federation. The vulnerability has been exploited in the wild.**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.127.0 (2025-03-25)
|
||||||
|
|
||||||
|
No significant changes since 1.127.0rc1.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.127.0rc1 (2025-03-18)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Update [MSC4140](https://github.com/matrix-org/matrix-spec-proposals/pull/4140) implementation to no longer cancel a user's own delayed state events with an event type & state key that match a more recent state event sent by that user. ([\#17810](https://github.com/element-hq/synapse/issues/17810))
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
|
||||||
|
- Fixed a minor typo in the Synapse documentation. Contributed by @karuto12. ([\#18224](https://github.com/element-hq/synapse/issues/18224))
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Remove undocumented `SYNAPSE_USE_FROZEN_DICTS` environment variable. ([\#18123](https://github.com/element-hq/synapse/issues/18123))
|
||||||
|
- Fix detection of workflow failures in the release script. ([\#18211](https://github.com/element-hq/synapse/issues/18211))
|
||||||
|
- Add caching support to media endpoints. ([\#18235](https://github.com/element-hq/synapse/issues/18235))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Updates to locked dependencies
|
||||||
|
|
||||||
|
* Bump anyhow from 1.0.96 to 1.0.97. ([\#18201](https://github.com/element-hq/synapse/issues/18201))
|
||||||
|
* Bump bcrypt from 4.2.1 to 4.3.0. ([\#18207](https://github.com/element-hq/synapse/issues/18207))
|
||||||
|
* Bump bytes from 1.10.0 to 1.10.1. ([\#18227](https://github.com/element-hq/synapse/issues/18227))
|
||||||
|
* Bump http from 1.2.0 to 1.3.1. ([\#18245](https://github.com/element-hq/synapse/issues/18245))
|
||||||
|
* Bump sentry-sdk from 2.19.2 to 2.22.0. ([\#18205](https://github.com/element-hq/synapse/issues/18205))
|
||||||
|
* Bump serde from 1.0.218 to 1.0.219. ([\#18228](https://github.com/element-hq/synapse/issues/18228))
|
||||||
|
* Bump serde_json from 1.0.139 to 1.0.140. ([\#18202](https://github.com/element-hq/synapse/issues/18202))
|
||||||
|
* Bump ulid from 1.2.0 to 1.2.1. ([\#18246](https://github.com/element-hq/synapse/issues/18246))
|
||||||
|
|
||||||
|
# Synapse 1.126.0 (2025-03-11)
|
||||||
Administrators using the Debian/Ubuntu packages from `packages.matrix.org`, please check
|
Administrators using the Debian/Ubuntu packages from `packages.matrix.org`, please check
|
||||||
[the relevant section in the upgrade notes](https://github.com/element-hq/synapse/blob/release-v1.126/docs/upgrade.md#change-of-signing-key-expiry-date-for-the-debianubuntu-package-repository)
|
[the relevant section in the upgrade notes](https://github.com/element-hq/synapse/blob/release-v1.126/docs/upgrade.md#change-of-signing-key-expiry-date-for-the-debianubuntu-package-repository)
|
||||||
as we have recently updated the expiry date on the repository's GPG signing key. The old version of the key will expire on `2025-03-15`.
|
as we have recently updated the expiry date on the repository's GPG signing key. The old version of the key will expire on `2025-03-15`.
|
||||||
|
|
||||||
|
No significant changes since 1.126.0rc3.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.126.0rc3 (2025-03-07)
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
|
||||||
|
- Revert the background job to clear unreferenced state groups (that was introduced in v1.126.0rc1), due to [a suspected issue](https://github.com/element-hq/synapse/issues/18217) that causes increased disk usage. ([\#18222](https://github.com/element-hq/synapse/issues/18222))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Synapse 1.126.0rc2 (2025-03-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Internal Changes
|
||||||
|
|
||||||
|
- Fix wheel building configuration in CI by installing libatomic1. ([\#18212](https://github.com/element-hq/synapse/issues/18212), [\#18213](https://github.com/element-hq/synapse/issues/18213))
|
||||||
|
|
||||||
|
# Synapse 1.126.0rc1 (2025-03-04)
|
||||||
|
|
||||||
|
Synapse 1.126.0rc1 was not fully released due to an error in CI.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
1314
Cargo.lock
generated
1314
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
README.rst
12
README.rst
@ -253,15 +253,17 @@ Alongside all that, join our developer community on Matrix:
|
|||||||
Copyright and Licensing
|
Copyright and Licensing
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
Copyright 2014-2017 OpenMarket Ltd
|
| Copyright 2014-2017 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
| Copyright 2017 Vector Creations Ltd
|
||||||
Copyright 2017-2025 New Vector Ltd
|
| Copyright 2017-2025 New Vector Ltd
|
||||||
|
|
|
||||||
|
|
||||||
This software is dual-licensed by New Vector Ltd (Element). It can be used either:
|
This software is dual-licensed by New Vector Ltd (Element). It can be used either:
|
||||||
|
|
||||||
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
|
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
|
||||||
|
|
||||||
(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
|
(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.
|
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.
|
||||||
|
|
||||||
|
|
||||||
|
1
changelog.d/18070.feature
Normal file
1
changelog.d/18070.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Support for [MSC4235](https://github.com/matrix-org/matrix-spec-proposals/pull/4235): via query param for hierarchy endpoint. Contributed by Krishan (@kfiven).
|
1
changelog.d/18196.feature
Normal file
1
changelog.d/18196.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Add `forget_forced_upon_leave` capability as per [MSC4267](https://github.com/matrix-org/matrix-spec-proposals/pull/4267).
|
1
changelog.d/18241.feature
Normal file
1
changelog.d/18241.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Add `federated_user_may_invite` spam checker callback which receives the entire invite event. Contributed by @tulir @ Beeper.
|
1
changelog.d/18509.bugfix
Normal file
1
changelog.d/18509.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fix `KeyError` on background updates when using split main/state databases.
|
1
changelog.d/18573.misc
Normal file
1
changelog.d/18573.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Improve docstring on `simple_upsert_many`.
|
1
changelog.d/18582.bugfix
Normal file
1
changelog.d/18582.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Improve performance of device deletion by adding missing index.
|
1
changelog.d/18595.misc
Normal file
1
changelog.d/18595.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Better handling of ratelimited requests.
|
1
changelog.d/18600.misc
Normal file
1
changelog.d/18600.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Better handling of ratelimited requests.
|
1
changelog.d/18602.misc
Normal file
1
changelog.d/18602.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Speed up bulk device deletion.
|
1
changelog.d/18605.bugfix
Normal file
1
changelog.d/18605.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Ensure policy servers are not asked to scan policy server change events, allowing rooms to disable the use of a policy server while the policy server is down.
|
@ -220,29 +220,24 @@
|
|||||||
"yBucketBound": "auto"
|
"yBucketBound": "auto"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"aliasColors": {},
|
|
||||||
"bars": false,
|
|
||||||
"dashLength": 10,
|
|
||||||
"dashes": false,
|
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"uid": "${DS_PROMETHEUS}"
|
"uid": "${DS_PROMETHEUS}",
|
||||||
|
"type": "prometheus"
|
||||||
},
|
},
|
||||||
"description": "",
|
"aliasColors": {},
|
||||||
|
"dashLength": 10,
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"links": []
|
"links": []
|
||||||
},
|
},
|
||||||
"overrides": []
|
"overrides": []
|
||||||
},
|
},
|
||||||
"fill": 0,
|
|
||||||
"fillGradient": 0,
|
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"h": 9,
|
"h": 9,
|
||||||
"w": 12,
|
"w": 12,
|
||||||
"x": 12,
|
"x": 12,
|
||||||
"y": 1
|
"y": 1
|
||||||
},
|
},
|
||||||
"hiddenSeries": false,
|
|
||||||
"id": 152,
|
"id": 152,
|
||||||
"legend": {
|
"legend": {
|
||||||
"avg": false,
|
"avg": false,
|
||||||
@ -255,71 +250,81 @@
|
|||||||
"values": false
|
"values": false
|
||||||
},
|
},
|
||||||
"lines": true,
|
"lines": true,
|
||||||
"linewidth": 0,
|
|
||||||
"links": [],
|
|
||||||
"nullPointMode": "connected",
|
"nullPointMode": "connected",
|
||||||
"options": {
|
"options": {
|
||||||
"alertThreshold": true
|
"alertThreshold": true
|
||||||
},
|
},
|
||||||
"paceLength": 10,
|
"paceLength": 10,
|
||||||
"percentage": false,
|
"pluginVersion": "10.4.3",
|
||||||
"pluginVersion": "9.2.2",
|
|
||||||
"pointradius": 5,
|
"pointradius": 5,
|
||||||
"points": false,
|
|
||||||
"renderer": "flot",
|
"renderer": "flot",
|
||||||
"seriesOverrides": [
|
"seriesOverrides": [
|
||||||
{
|
{
|
||||||
"alias": "Avg",
|
"alias": "Avg",
|
||||||
"fill": 0,
|
"fill": 0,
|
||||||
"linewidth": 3
|
"linewidth": 3,
|
||||||
|
"$$hashKey": "object:48"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": "99%",
|
"alias": "99%",
|
||||||
"color": "#C4162A",
|
"color": "#C4162A",
|
||||||
"fillBelowTo": "90%"
|
"fillBelowTo": "90%",
|
||||||
|
"$$hashKey": "object:49"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": "90%",
|
"alias": "90%",
|
||||||
"color": "#FF7383",
|
"color": "#FF7383",
|
||||||
"fillBelowTo": "75%"
|
"fillBelowTo": "75%",
|
||||||
|
"$$hashKey": "object:50"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": "75%",
|
"alias": "75%",
|
||||||
"color": "#FFEE52",
|
"color": "#FFEE52",
|
||||||
"fillBelowTo": "50%"
|
"fillBelowTo": "50%",
|
||||||
|
"$$hashKey": "object:51"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": "50%",
|
"alias": "50%",
|
||||||
"color": "#73BF69",
|
"color": "#73BF69",
|
||||||
"fillBelowTo": "25%"
|
"fillBelowTo": "25%",
|
||||||
|
"$$hashKey": "object:52"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": "25%",
|
"alias": "25%",
|
||||||
"color": "#1F60C4",
|
"color": "#1F60C4",
|
||||||
"fillBelowTo": "5%"
|
"fillBelowTo": "5%",
|
||||||
|
"$$hashKey": "object:53"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": "5%",
|
"alias": "5%",
|
||||||
"lines": false
|
"lines": false,
|
||||||
|
"$$hashKey": "object:54"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": "Average",
|
"alias": "Average",
|
||||||
"color": "rgb(255, 255, 255)",
|
"color": "rgb(255, 255, 255)",
|
||||||
"lines": true,
|
"lines": true,
|
||||||
"linewidth": 3
|
"linewidth": 3,
|
||||||
|
"$$hashKey": "object:55"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": "Events",
|
"alias": "Local events being persisted",
|
||||||
|
"color": "#96d98D",
|
||||||
|
"points": true,
|
||||||
|
"yaxis": 2,
|
||||||
|
"zindex": -3,
|
||||||
|
"$$hashKey": "object:56"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:329",
|
||||||
"color": "#B877D9",
|
"color": "#B877D9",
|
||||||
"hideTooltip": true,
|
"alias": "All events being persisted",
|
||||||
"points": true,
|
"points": true,
|
||||||
"yaxis": 2,
|
"yaxis": 2,
|
||||||
"zindex": -3
|
"zindex": -3
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"spaceLength": 10,
|
"spaceLength": 10,
|
||||||
"stack": false,
|
|
||||||
"steppedLine": false,
|
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"datasource": {
|
"datasource": {
|
||||||
@ -384,7 +389,20 @@
|
|||||||
},
|
},
|
||||||
"expr": "sum(rate(synapse_http_server_response_time_seconds_sum{servlet='RoomSendEventRestServlet',index=~\"$index\",instance=\"$instance\",code=~\"2..\"}[$bucket_size])) / sum(rate(synapse_http_server_response_time_seconds_count{servlet='RoomSendEventRestServlet',index=~\"$index\",instance=\"$instance\",code=~\"2..\"}[$bucket_size]))",
|
"expr": "sum(rate(synapse_http_server_response_time_seconds_sum{servlet='RoomSendEventRestServlet',index=~\"$index\",instance=\"$instance\",code=~\"2..\"}[$bucket_size])) / sum(rate(synapse_http_server_response_time_seconds_count{servlet='RoomSendEventRestServlet',index=~\"$index\",instance=\"$instance\",code=~\"2..\"}[$bucket_size]))",
|
||||||
"legendFormat": "Average",
|
"legendFormat": "Average",
|
||||||
"refId": "H"
|
"refId": "H",
|
||||||
|
"editorMode": "code",
|
||||||
|
"range": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"uid": "${DS_PROMETHEUS}"
|
||||||
|
},
|
||||||
|
"expr": "sum(rate(synapse_http_server_response_time_seconds_count{servlet='RoomSendEventRestServlet',index=~\"$index\",instance=\"$instance\",code=~\"2..\"}[$bucket_size]))",
|
||||||
|
"hide": false,
|
||||||
|
"instant": false,
|
||||||
|
"legendFormat": "Local events being persisted",
|
||||||
|
"refId": "E",
|
||||||
|
"editorMode": "code"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"datasource": {
|
"datasource": {
|
||||||
@ -393,8 +411,9 @@
|
|||||||
"expr": "sum(rate(synapse_storage_events_persisted_events_total{instance=\"$instance\"}[$bucket_size]))",
|
"expr": "sum(rate(synapse_storage_events_persisted_events_total{instance=\"$instance\"}[$bucket_size]))",
|
||||||
"hide": false,
|
"hide": false,
|
||||||
"instant": false,
|
"instant": false,
|
||||||
"legendFormat": "Events",
|
"legendFormat": "All events being persisted",
|
||||||
"refId": "E"
|
"refId": "I",
|
||||||
|
"editorMode": "code"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"thresholds": [
|
"thresholds": [
|
||||||
@ -428,7 +447,9 @@
|
|||||||
"xaxis": {
|
"xaxis": {
|
||||||
"mode": "time",
|
"mode": "time",
|
||||||
"show": true,
|
"show": true,
|
||||||
"values": []
|
"values": [],
|
||||||
|
"name": null,
|
||||||
|
"buckets": null
|
||||||
},
|
},
|
||||||
"yaxes": [
|
"yaxes": [
|
||||||
{
|
{
|
||||||
@ -450,7 +471,20 @@
|
|||||||
],
|
],
|
||||||
"yaxis": {
|
"yaxis": {
|
||||||
"align": false
|
"align": false
|
||||||
}
|
},
|
||||||
|
"bars": false,
|
||||||
|
"dashes": false,
|
||||||
|
"description": "",
|
||||||
|
"fill": 0,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"linewidth": 0,
|
||||||
|
"percentage": false,
|
||||||
|
"points": false,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"aliasColors": {},
|
"aliasColors": {},
|
||||||
|
2
debian/build_virtualenv
vendored
2
debian/build_virtualenv
vendored
@ -35,7 +35,7 @@ TEMP_VENV="$(mktemp -d)"
|
|||||||
python3 -m venv "$TEMP_VENV"
|
python3 -m venv "$TEMP_VENV"
|
||||||
source "$TEMP_VENV/bin/activate"
|
source "$TEMP_VENV/bin/activate"
|
||||||
pip install -U pip
|
pip install -U pip
|
||||||
pip install poetry==1.3.2
|
pip install poetry==2.1.1 poetry-plugin-export==1.9.0
|
||||||
poetry export \
|
poetry export \
|
||||||
--extras all \
|
--extras all \
|
||||||
--extras test \
|
--extras test \
|
||||||
|
109
debian/changelog
vendored
109
debian/changelog
vendored
@ -1,3 +1,112 @@
|
|||||||
|
matrix-synapse-py3 (1.133.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.133.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 24 Jun 2025 11:57:47 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.132.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.132.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 17 Jun 2025 13:16:20 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.132.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.132.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 10 Jun 2025 11:15:18 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.131.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.131.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 03 Jun 2025 14:36:55 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.131.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New synapse release 1.131.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Wed, 28 May 2025 10:25:44 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.130.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.130.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 20 May 2025 08:34:13 -0600
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.130.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.130.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 13 May 2025 10:44:04 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.129.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.129.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 06 May 2025 12:22:11 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.129.0~rc2) stable; urgency=medium
|
||||||
|
|
||||||
|
* New synapse release 1.129.0rc2.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Wed, 30 Apr 2025 13:13:16 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.129.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.129.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 15 Apr 2025 10:47:43 -0600
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.128.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.128.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 08 Apr 2025 14:09:54 +0100
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.128.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* Update Poetry to 2.1.1.
|
||||||
|
* New synapse release 1.128.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 01 Apr 2025 14:35:33 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.127.1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.127.1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Wed, 26 Mar 2025 21:07:31 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.127.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.127.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 25 Mar 2025 12:04:15 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.127.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.127.0rc1.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 18 Mar 2025 13:30:05 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.126.0) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.126.0.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Tue, 11 Mar 2025 13:11:29 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.126.0~rc3) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.126.0rc3.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Fri, 07 Mar 2025 15:45:05 +0000
|
||||||
|
|
||||||
|
matrix-synapse-py3 (1.126.0~rc2) stable; urgency=medium
|
||||||
|
|
||||||
|
* New Synapse release 1.126.0rc2.
|
||||||
|
|
||||||
|
-- Synapse Packaging team <packages@matrix.org> Wed, 05 Mar 2025 14:29:12 +0000
|
||||||
|
|
||||||
matrix-synapse-py3 (1.126.0~rc1) stable; urgency=medium
|
matrix-synapse-py3 (1.126.0~rc1) stable; urgency=medium
|
||||||
|
|
||||||
* New Synapse release 1.126.0rc1.
|
* New Synapse release 1.126.0rc1.
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
ARG DEBIAN_VERSION=bookworm
|
ARG DEBIAN_VERSION=bookworm
|
||||||
ARG PYTHON_VERSION=3.12
|
ARG PYTHON_VERSION=3.12
|
||||||
ARG POETRY_VERSION=1.8.3
|
ARG POETRY_VERSION=2.1.1
|
||||||
|
|
||||||
###
|
###
|
||||||
### Stage 0: generate requirements.txt
|
### Stage 0: generate requirements.txt
|
||||||
@ -56,7 +56,7 @@ ENV UV_LINK_MODE=copy
|
|||||||
ARG POETRY_VERSION
|
ARG POETRY_VERSION
|
||||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
if [ -z "$TEST_ONLY_IGNORE_POETRY_LOCKFILE" ]; then \
|
if [ -z "$TEST_ONLY_IGNORE_POETRY_LOCKFILE" ]; then \
|
||||||
uvx --with poetry-plugin-export==1.8.0 \
|
uvx --with poetry-plugin-export==1.9.0 \
|
||||||
poetry@${POETRY_VERSION} export --extras all -o /synapse/requirements.txt ${TEST_ONLY_SKIP_DEP_HASH_VERIFICATION:+--without-hashes}; \
|
poetry@${POETRY_VERSION} export --extras all -o /synapse/requirements.txt ${TEST_ONLY_SKIP_DEP_HASH_VERIFICATION:+--without-hashes}; \
|
||||||
else \
|
else \
|
||||||
touch /synapse/requirements.txt; \
|
touch /synapse/requirements.txt; \
|
||||||
@ -134,7 +134,6 @@ RUN \
|
|||||||
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
apt-get update -qq && \
|
apt-get update -qq && \
|
||||||
apt-get install -y --no-install-recommends rsync && \
|
|
||||||
apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances --no-pre-depends \
|
apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances --no-pre-depends \
|
||||||
curl \
|
curl \
|
||||||
gosu \
|
gosu \
|
||||||
@ -148,14 +147,10 @@ RUN \
|
|||||||
for arch in arm64 amd64; do \
|
for arch in arm64 amd64; do \
|
||||||
mkdir -p /tmp/debs-${arch} && \
|
mkdir -p /tmp/debs-${arch} && \
|
||||||
cd /tmp/debs-${arch} && \
|
cd /tmp/debs-${arch} && \
|
||||||
apt-get download $(sed "s/$/:${arch}/" /tmp/pkg-list); \
|
apt-get -o APT::Architecture="${arch}" download $(cat /tmp/pkg-list); \
|
||||||
done
|
done
|
||||||
|
|
||||||
# Extract the debs for each architecture
|
# Extract the debs for each architecture
|
||||||
# On the runtime image, /lib is a symlink to /usr/lib, so we need to copy the
|
|
||||||
# libraries to the right place, else the `COPY` won't work.
|
|
||||||
# On amd64, we'll also have a /lib64 folder with ld-linux-x86-64.so.2, which is
|
|
||||||
# already present in the runtime image.
|
|
||||||
RUN \
|
RUN \
|
||||||
for arch in arm64 amd64; do \
|
for arch in arm64 amd64; do \
|
||||||
mkdir -p /install-${arch}/var/lib/dpkg/status.d/ && \
|
mkdir -p /install-${arch}/var/lib/dpkg/status.d/ && \
|
||||||
@ -165,8 +160,6 @@ RUN \
|
|||||||
dpkg --ctrl-tarfile $deb | tar -Ox ./control > /install-${arch}/var/lib/dpkg/status.d/${package_name}; \
|
dpkg --ctrl-tarfile $deb | tar -Ox ./control > /install-${arch}/var/lib/dpkg/status.d/${package_name}; \
|
||||||
dpkg --extract $deb /install-${arch}; \
|
dpkg --extract $deb /install-${arch}; \
|
||||||
done; \
|
done; \
|
||||||
rsync -avr /install-${arch}/lib/ /install-${arch}/usr/lib; \
|
|
||||||
rm -rf /install-${arch}/lib /install-${arch}/lib64; \
|
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
@ -183,7 +176,14 @@ LABEL org.opencontainers.image.documentation='https://github.com/element-hq/syna
|
|||||||
LABEL org.opencontainers.image.source='https://github.com/element-hq/synapse.git'
|
LABEL org.opencontainers.image.source='https://github.com/element-hq/synapse.git'
|
||||||
LABEL org.opencontainers.image.licenses='AGPL-3.0-or-later'
|
LABEL org.opencontainers.image.licenses='AGPL-3.0-or-later'
|
||||||
|
|
||||||
COPY --from=runtime-deps /install-${TARGETARCH} /
|
# On the runtime image, /lib is a symlink to /usr/lib, so we need to copy the
|
||||||
|
# libraries to the right place, else the `COPY` won't work.
|
||||||
|
# On amd64, we'll also have a /lib64 folder with ld-linux-x86-64.so.2, which is
|
||||||
|
# already present in the runtime image.
|
||||||
|
COPY --from=runtime-deps /install-${TARGETARCH}/lib /usr/lib
|
||||||
|
COPY --from=runtime-deps /install-${TARGETARCH}/etc /etc
|
||||||
|
COPY --from=runtime-deps /install-${TARGETARCH}/usr /usr
|
||||||
|
COPY --from=runtime-deps /install-${TARGETARCH}/var /var
|
||||||
COPY --from=builder /install /usr/local
|
COPY --from=builder /install /usr/local
|
||||||
COPY ./docker/start.py /start.py
|
COPY ./docker/start.py /start.py
|
||||||
COPY ./docker/conf /conf
|
COPY ./docker/conf /conf
|
||||||
|
@ -2,18 +2,38 @@
|
|||||||
|
|
||||||
ARG SYNAPSE_VERSION=latest
|
ARG SYNAPSE_VERSION=latest
|
||||||
ARG FROM=matrixdotorg/synapse:$SYNAPSE_VERSION
|
ARG FROM=matrixdotorg/synapse:$SYNAPSE_VERSION
|
||||||
|
ARG DEBIAN_VERSION=bookworm
|
||||||
|
ARG PYTHON_VERSION=3.12
|
||||||
|
|
||||||
# first of all, we create a base image with an nginx which we can copy into the
|
# first of all, we create a base image with dependencies which we can copy into the
|
||||||
# target image. For repeated rebuilds, this is much faster than apt installing
|
# target image. For repeated rebuilds, this is much faster than apt installing
|
||||||
# each time.
|
# each time.
|
||||||
|
|
||||||
FROM docker.io/library/debian:bookworm-slim AS deps_base
|
FROM ghcr.io/astral-sh/uv:python${PYTHON_VERSION}-${DEBIAN_VERSION} AS deps_base
|
||||||
|
|
||||||
|
# Tell apt to keep downloaded package files, as we're using cache mounts.
|
||||||
|
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
apt-get update -qq && \
|
apt-get update -qq && \
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -yqq --no-install-recommends \
|
DEBIAN_FRONTEND=noninteractive apt-get install -yqq --no-install-recommends \
|
||||||
redis-server nginx-light
|
nginx-light
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
# remove default page
|
||||||
|
rm /etc/nginx/sites-enabled/default && \
|
||||||
|
# have nginx log to stderr/out
|
||||||
|
ln -sf /dev/stdout /var/log/nginx/access.log && \
|
||||||
|
ln -sf /dev/stderr /var/log/nginx/error.log
|
||||||
|
|
||||||
|
# --link-mode=copy silences a warning as uv isn't able to do hardlinks between its cache
|
||||||
|
# (mounted as --mount=type=cache) and the target directory.
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
|
uv pip install --link-mode=copy --prefix="/uv/usr/local" supervisor~=4.2
|
||||||
|
|
||||||
|
RUN mkdir -p /uv/etc/supervisor/conf.d
|
||||||
|
|
||||||
# Similarly, a base to copy the redis server from.
|
# Similarly, a base to copy the redis server from.
|
||||||
#
|
#
|
||||||
@ -21,31 +41,21 @@ FROM docker.io/library/debian:bookworm-slim AS deps_base
|
|||||||
# which makes it much easier to copy (but we need to make sure we use an image
|
# which makes it much easier to copy (but we need to make sure we use an image
|
||||||
# based on the same debian version as the synapse image, to make sure we get
|
# based on the same debian version as the synapse image, to make sure we get
|
||||||
# the expected version of libc.
|
# the expected version of libc.
|
||||||
FROM docker.io/library/redis:7-bookworm AS redis_base
|
FROM docker.io/library/redis:7-${DEBIAN_VERSION} AS redis_base
|
||||||
|
|
||||||
# now build the final image, based on the the regular Synapse docker image
|
# now build the final image, based on the the regular Synapse docker image
|
||||||
FROM $FROM
|
FROM $FROM
|
||||||
|
|
||||||
# Install supervisord with pip instead of apt, to avoid installing a second
|
# Copy over dependencies
|
||||||
# copy of python.
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
|
||||||
pip install supervisor~=4.2
|
|
||||||
RUN mkdir -p /etc/supervisor/conf.d
|
|
||||||
|
|
||||||
# Copy over redis and nginx
|
|
||||||
COPY --from=redis_base /usr/local/bin/redis-server /usr/local/bin
|
COPY --from=redis_base /usr/local/bin/redis-server /usr/local/bin
|
||||||
|
COPY --from=deps_base /uv /
|
||||||
COPY --from=deps_base /usr/sbin/nginx /usr/sbin
|
COPY --from=deps_base /usr/sbin/nginx /usr/sbin
|
||||||
COPY --from=deps_base /usr/share/nginx /usr/share/nginx
|
COPY --from=deps_base /usr/share/nginx /usr/share/nginx
|
||||||
COPY --from=deps_base /usr/lib/nginx /usr/lib/nginx
|
COPY --from=deps_base /usr/lib/nginx /usr/lib/nginx
|
||||||
COPY --from=deps_base /etc/nginx /etc/nginx
|
COPY --from=deps_base /etc/nginx /etc/nginx
|
||||||
RUN rm /etc/nginx/sites-enabled/default
|
COPY --from=deps_base /var/log/nginx /var/log/nginx
|
||||||
RUN mkdir /var/log/nginx /var/lib/nginx
|
# chown to allow non-root user to write to http-*-temp-path dirs
|
||||||
RUN chown www-data /var/lib/nginx
|
COPY --from=deps_base --chown=www-data:root /var/lib/nginx /var/lib/nginx
|
||||||
|
|
||||||
# have nginx log to stderr/out
|
|
||||||
RUN ln -sf /dev/stdout /var/log/nginx/access.log
|
|
||||||
RUN ln -sf /dev/stderr /var/log/nginx/error.log
|
|
||||||
|
|
||||||
# Copy Synapse worker, nginx and supervisord configuration template files
|
# Copy Synapse worker, nginx and supervisord configuration template files
|
||||||
COPY ./docker/conf-workers/* /conf/
|
COPY ./docker/conf-workers/* /conf/
|
||||||
@ -64,4 +74,4 @@ FROM $FROM
|
|||||||
# Replace the healthcheck with one which checks *all* the workers. The script
|
# Replace the healthcheck with one which checks *all* the workers. The script
|
||||||
# is generated by configure_workers_and_start.py.
|
# is generated by configure_workers_and_start.py.
|
||||||
HEALTHCHECK --start-period=5s --interval=15s --timeout=5s \
|
HEALTHCHECK --start-period=5s --interval=15s --timeout=5s \
|
||||||
CMD /bin/sh /healthcheck.sh
|
CMD ["/healthcheck.sh"]
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
ARG SYNAPSE_VERSION=latest
|
ARG SYNAPSE_VERSION=latest
|
||||||
# This is an intermediate image, to be built locally (not pulled from a registry).
|
# This is an intermediate image, to be built locally (not pulled from a registry).
|
||||||
ARG FROM=matrixdotorg/synapse-workers:$SYNAPSE_VERSION
|
ARG FROM=matrixdotorg/synapse-workers:$SYNAPSE_VERSION
|
||||||
|
ARG DEBIAN_VERSION=bookworm
|
||||||
|
|
||||||
|
FROM docker.io/library/postgres:13-${DEBIAN_VERSION} AS postgres_base
|
||||||
|
|
||||||
FROM $FROM
|
FROM $FROM
|
||||||
# First of all, we copy postgres server from the official postgres image,
|
# First of all, we copy postgres server from the official postgres image,
|
||||||
@ -20,9 +23,9 @@ FROM $FROM
|
|||||||
# the same debian version as Synapse's docker image (so the versions of the
|
# the same debian version as Synapse's docker image (so the versions of the
|
||||||
# shared libraries match).
|
# shared libraries match).
|
||||||
RUN adduser --system --uid 999 postgres --home /var/lib/postgresql
|
RUN adduser --system --uid 999 postgres --home /var/lib/postgresql
|
||||||
COPY --from=docker.io/library/postgres:13-bookworm /usr/lib/postgresql /usr/lib/postgresql
|
COPY --from=postgres_base /usr/lib/postgresql /usr/lib/postgresql
|
||||||
COPY --from=docker.io/library/postgres:13-bookworm /usr/share/postgresql /usr/share/postgresql
|
COPY --from=postgres_base /usr/share/postgresql /usr/share/postgresql
|
||||||
RUN mkdir /var/run/postgresql && chown postgres /var/run/postgresql
|
COPY --from=postgres_base --chown=postgres /var/run/postgresql /var/run/postgresql
|
||||||
ENV PATH="${PATH}:/usr/lib/postgresql/13/bin"
|
ENV PATH="${PATH}:/usr/lib/postgresql/13/bin"
|
||||||
ENV PGDATA=/var/lib/postgresql/data
|
ENV PGDATA=/var/lib/postgresql/data
|
||||||
|
|
||||||
@ -55,4 +58,4 @@ ENTRYPOINT ["/start_for_complement.sh"]
|
|||||||
|
|
||||||
# Update the healthcheck to have a shorter check interval
|
# Update the healthcheck to have a shorter check interval
|
||||||
HEALTHCHECK --start-period=5s --interval=1s --timeout=1s \
|
HEALTHCHECK --start-period=5s --interval=1s --timeout=1s \
|
||||||
CMD /bin/sh /healthcheck.sh
|
CMD ["/healthcheck.sh"]
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "Complement Synapse launcher"
|
echo "Complement Synapse launcher"
|
||||||
echo " Args: $@"
|
echo " Args: $*"
|
||||||
echo " Env: SYNAPSE_COMPLEMENT_DATABASE=$SYNAPSE_COMPLEMENT_DATABASE SYNAPSE_COMPLEMENT_USE_WORKERS=$SYNAPSE_COMPLEMENT_USE_WORKERS SYNAPSE_COMPLEMENT_USE_ASYNCIO_REACTOR=$SYNAPSE_COMPLEMENT_USE_ASYNCIO_REACTOR"
|
echo " Env: SYNAPSE_COMPLEMENT_DATABASE=$SYNAPSE_COMPLEMENT_DATABASE SYNAPSE_COMPLEMENT_USE_WORKERS=$SYNAPSE_COMPLEMENT_USE_WORKERS SYNAPSE_COMPLEMENT_USE_ASYNCIO_REACTOR=$SYNAPSE_COMPLEMENT_USE_ASYNCIO_REACTOR"
|
||||||
|
|
||||||
function log {
|
function log {
|
||||||
d=$(date +"%Y-%m-%d %H:%M:%S,%3N")
|
d=$(printf '%(%Y-%m-%d %H:%M:%S)T,%.3s\n' ${EPOCHREALTIME/./ })
|
||||||
echo "$d $@"
|
echo "$d $*"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set the server name of the homeserver
|
# Set the server name of the homeserver
|
||||||
@ -103,12 +103,11 @@ fi
|
|||||||
# Note that both the key and certificate are in PEM format (not DER).
|
# Note that both the key and certificate are in PEM format (not DER).
|
||||||
|
|
||||||
# First generate a configuration file to set up a Subject Alternative Name.
|
# First generate a configuration file to set up a Subject Alternative Name.
|
||||||
cat > /conf/server.tls.conf <<EOF
|
echo "\
|
||||||
.include /etc/ssl/openssl.cnf
|
.include /etc/ssl/openssl.cnf
|
||||||
|
|
||||||
[SAN]
|
[SAN]
|
||||||
subjectAltName=DNS:${SERVER_NAME}
|
subjectAltName=DNS:${SERVER_NAME}" > /conf/server.tls.conf
|
||||||
EOF
|
|
||||||
|
|
||||||
# Generate an RSA key
|
# Generate an RSA key
|
||||||
openssl genrsa -out /conf/server.tls.key 2048
|
openssl genrsa -out /conf/server.tls.key 2048
|
||||||
@ -123,12 +122,12 @@ openssl x509 -req -in /conf/server.tls.csr \
|
|||||||
-out /conf/server.tls.crt -extfile /conf/server.tls.conf -extensions SAN
|
-out /conf/server.tls.crt -extfile /conf/server.tls.conf -extensions SAN
|
||||||
|
|
||||||
# Assert that we have a Subject Alternative Name in the certificate.
|
# Assert that we have a Subject Alternative Name in the certificate.
|
||||||
# (grep will exit with 1 here if there isn't a SAN in the certificate.)
|
# (the test will exit with 1 here if there isn't a SAN in the certificate.)
|
||||||
openssl x509 -in /conf/server.tls.crt -noout -text | grep DNS:
|
[[ $(openssl x509 -in /conf/server.tls.crt -noout -text) == *DNS:* ]]
|
||||||
|
|
||||||
export SYNAPSE_TLS_CERT=/conf/server.tls.crt
|
export SYNAPSE_TLS_CERT=/conf/server.tls.crt
|
||||||
export SYNAPSE_TLS_KEY=/conf/server.tls.key
|
export SYNAPSE_TLS_KEY=/conf/server.tls.key
|
||||||
|
|
||||||
# Run the script that writes the necessary config files and starts supervisord, which in turn
|
# Run the script that writes the necessary config files and starts supervisord, which in turn
|
||||||
# starts everything else
|
# starts everything else
|
||||||
exec /configure_workers_and_start.py
|
exec /configure_workers_and_start.py "$@"
|
||||||
|
@ -127,6 +127,8 @@ experimental_features:
|
|||||||
msc3983_appservice_otk_claims: true
|
msc3983_appservice_otk_claims: true
|
||||||
# Proxy key queries to exclusive ASes
|
# Proxy key queries to exclusive ASes
|
||||||
msc3984_appservice_key_query: true
|
msc3984_appservice_key_query: true
|
||||||
|
# Invite filtering
|
||||||
|
msc4155_enabled: true
|
||||||
|
|
||||||
server_notices:
|
server_notices:
|
||||||
system_mxid_localpart: _server
|
system_mxid_localpart: _server
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/local/bin/python
|
||||||
#
|
#
|
||||||
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||||
#
|
#
|
||||||
@ -202,6 +202,7 @@ WORKERS_CONFIG: Dict[str, Dict[str, Any]] = {
|
|||||||
"app": "synapse.app.generic_worker",
|
"app": "synapse.app.generic_worker",
|
||||||
"listener_resources": ["federation"],
|
"listener_resources": ["federation"],
|
||||||
"endpoint_patterns": [
|
"endpoint_patterns": [
|
||||||
|
"^/_matrix/federation/v1/version$",
|
||||||
"^/_matrix/federation/(v1|v2)/event/",
|
"^/_matrix/federation/(v1|v2)/event/",
|
||||||
"^/_matrix/federation/(v1|v2)/state/",
|
"^/_matrix/federation/(v1|v2)/state/",
|
||||||
"^/_matrix/federation/(v1|v2)/state_ids/",
|
"^/_matrix/federation/(v1|v2)/state_ids/",
|
||||||
@ -351,6 +352,11 @@ def error(txt: str) -> NoReturn:
|
|||||||
|
|
||||||
|
|
||||||
def flush_buffers() -> None:
|
def flush_buffers() -> None:
|
||||||
|
"""
|
||||||
|
Python's `print()` buffers output by default, typically waiting until ~8KB
|
||||||
|
accumulates. This method can be used to flush the buffers so we can see the output
|
||||||
|
of any print statements so far.
|
||||||
|
"""
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
@ -376,9 +382,11 @@ def convert(src: str, dst: str, **template_vars: object) -> None:
|
|||||||
#
|
#
|
||||||
# We use append mode in case the files have already been written to by something else
|
# We use append mode in case the files have already been written to by something else
|
||||||
# (for instance, as part of the instructions in a dockerfile).
|
# (for instance, as part of the instructions in a dockerfile).
|
||||||
|
exists = os.path.isfile(dst)
|
||||||
with open(dst, "a") as outfile:
|
with open(dst, "a") as outfile:
|
||||||
# In case the existing file doesn't end with a newline
|
# In case the existing file doesn't end with a newline
|
||||||
outfile.write("\n")
|
if exists:
|
||||||
|
outfile.write("\n")
|
||||||
|
|
||||||
outfile.write(rendered)
|
outfile.write(rendered)
|
||||||
|
|
||||||
@ -604,7 +612,7 @@ def generate_base_homeserver_config() -> None:
|
|||||||
# start.py already does this for us, so just call that.
|
# start.py already does this for us, so just call that.
|
||||||
# note that this script is copied in in the official, monolith dockerfile
|
# note that this script is copied in in the official, monolith dockerfile
|
||||||
os.environ["SYNAPSE_HTTP_PORT"] = str(MAIN_PROCESS_HTTP_LISTENER_PORT)
|
os.environ["SYNAPSE_HTTP_PORT"] = str(MAIN_PROCESS_HTTP_LISTENER_PORT)
|
||||||
subprocess.run(["/usr/local/bin/python", "/start.py", "migrate_config"], check=True)
|
subprocess.run([sys.executable, "/start.py", "migrate_config"], check=True)
|
||||||
|
|
||||||
|
|
||||||
def parse_worker_types(
|
def parse_worker_types(
|
||||||
@ -998,6 +1006,7 @@ def generate_worker_files(
|
|||||||
"/healthcheck.sh",
|
"/healthcheck.sh",
|
||||||
healthcheck_urls=healthcheck_urls,
|
healthcheck_urls=healthcheck_urls,
|
||||||
)
|
)
|
||||||
|
os.chmod("/healthcheck.sh", 0o755)
|
||||||
|
|
||||||
# Ensure the logging directory exists
|
# Ensure the logging directory exists
|
||||||
log_dir = data_dir + "/logs"
|
log_dir = data_dir + "/logs"
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
# '-W interactive' is a `mawk` extension which disables buffering on stdout and sets line-buffered reads on
|
# '-W interactive' is a `mawk` extension which disables buffering on stdout and sets line-buffered reads on
|
||||||
# stdin. The effect is that the output is flushed after each line, rather than being batched, which helps reduce
|
# stdin. The effect is that the output is flushed after each line, rather than being batched, which helps reduce
|
||||||
# confusion due to to interleaving of the different processes.
|
# confusion due to to interleaving of the different processes.
|
||||||
exec 1> >(awk -W interactive '{print "'"${SUPERVISOR_PROCESS_NAME}"' | "$0 }' >&1)
|
prefixer() {
|
||||||
exec 2> >(awk -W interactive '{print "'"${SUPERVISOR_PROCESS_NAME}"' | "$0 }' >&2)
|
mawk -W interactive '{printf("%s | %s\n", ENVIRON["SUPERVISOR_PROCESS_NAME"], $0); fflush() }'
|
||||||
|
}
|
||||||
|
exec 1> >(prefixer)
|
||||||
|
exec 2> >(prefixer >&2)
|
||||||
exec "$@"
|
exec "$@"
|
||||||
|
@ -22,6 +22,11 @@ def error(txt: str) -> NoReturn:
|
|||||||
|
|
||||||
|
|
||||||
def flush_buffers() -> None:
|
def flush_buffers() -> None:
|
||||||
|
"""
|
||||||
|
Python's `print()` buffers output by default, typically waiting until ~8KB
|
||||||
|
accumulates. This method can be used to flush the buffers so we can see the output
|
||||||
|
of any print statements so far.
|
||||||
|
"""
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
@ -63,6 +63,18 @@ mdbook serve
|
|||||||
|
|
||||||
The URL at which the docs can be viewed at will be logged.
|
The URL at which the docs can be viewed at will be logged.
|
||||||
|
|
||||||
|
## Synapse configuration documentation
|
||||||
|
|
||||||
|
The [Configuration
|
||||||
|
Manual](https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html)
|
||||||
|
page is generated from a YAML file,
|
||||||
|
[schema/synapse-config.schema.yaml](../schema/synapse-config.schema.yaml). To
|
||||||
|
add new options or modify existing ones, first edit that file, then run
|
||||||
|
[scripts-dev/gen_config_documentation.py](../scripts-dev/gen_config_documentation.py)
|
||||||
|
to generate an updated Configuration Manual markdown file.
|
||||||
|
|
||||||
|
Build the book as described above to preview it in a web browser.
|
||||||
|
|
||||||
## Configuration and theming
|
## Configuration and theming
|
||||||
|
|
||||||
The look and behaviour of the website is configured by the [book.toml](../book.toml) file
|
The look and behaviour of the website is configured by the [book.toml](../book.toml) file
|
||||||
|
@ -49,6 +49,8 @@
|
|||||||
- [Background update controller callbacks](modules/background_update_controller_callbacks.md)
|
- [Background update controller callbacks](modules/background_update_controller_callbacks.md)
|
||||||
- [Account data callbacks](modules/account_data_callbacks.md)
|
- [Account data callbacks](modules/account_data_callbacks.md)
|
||||||
- [Add extra fields to client events unsigned section callbacks](modules/add_extra_fields_to_client_events_unsigned.md)
|
- [Add extra fields to client events unsigned section callbacks](modules/add_extra_fields_to_client_events_unsigned.md)
|
||||||
|
- [Media repository callbacks](modules/media_repository_callbacks.md)
|
||||||
|
- [Ratelimit callbacks](modules/ratelimit_callbacks.md)
|
||||||
- [Porting a legacy module to the new interface](modules/porting_legacy_module.md)
|
- [Porting a legacy module to the new interface](modules/porting_legacy_module.md)
|
||||||
- [Workers](workers.md)
|
- [Workers](workers.md)
|
||||||
- [Using `synctl` with Workers](synctl_workers.md)
|
- [Using `synctl` with Workers](synctl_workers.md)
|
||||||
@ -66,6 +68,7 @@
|
|||||||
- [Registration Tokens](usage/administration/admin_api/registration_tokens.md)
|
- [Registration Tokens](usage/administration/admin_api/registration_tokens.md)
|
||||||
- [Manipulate Room Membership](admin_api/room_membership.md)
|
- [Manipulate Room Membership](admin_api/room_membership.md)
|
||||||
- [Rooms](admin_api/rooms.md)
|
- [Rooms](admin_api/rooms.md)
|
||||||
|
- [Scheduled tasks](admin_api/scheduled_tasks.md)
|
||||||
- [Server Notices](admin_api/server_notices.md)
|
- [Server Notices](admin_api/server_notices.md)
|
||||||
- [Statistics](admin_api/statistics.md)
|
- [Statistics](admin_api/statistics.md)
|
||||||
- [Users](admin_api/user_admin_api.md)
|
- [Users](admin_api/user_admin_api.md)
|
||||||
|
@ -46,6 +46,14 @@ 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.
|
The media file itself (and any thumbnails) is not deleted from the server.
|
||||||
|
|
||||||
|
Since Synapse 1.128.0, hashes of uploaded media are tracked. If this media
|
||||||
|
is quarantined, Synapse will:
|
||||||
|
|
||||||
|
- Quarantine any media with a matching hash that has already been uploaded.
|
||||||
|
- Quarantine any future media.
|
||||||
|
- Quarantine any existing cached remote media.
|
||||||
|
- Quarantine any future remote media.
|
||||||
|
|
||||||
## Quarantining media by ID
|
## Quarantining media by ID
|
||||||
|
|
||||||
This API quarantines a single piece of local or remote media.
|
This API quarantines a single piece of local or remote media.
|
||||||
|
@ -794,6 +794,7 @@ A response body like the following is returned:
|
|||||||
"results": [
|
"results": [
|
||||||
{
|
{
|
||||||
"delete_id": "delete_id1",
|
"delete_id": "delete_id1",
|
||||||
|
"room_id": "!roomid:example.com",
|
||||||
"status": "failed",
|
"status": "failed",
|
||||||
"error": "error message",
|
"error": "error message",
|
||||||
"shutdown_room": {
|
"shutdown_room": {
|
||||||
@ -804,6 +805,7 @@ A response body like the following is returned:
|
|||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
"delete_id": "delete_id2",
|
"delete_id": "delete_id2",
|
||||||
|
"room_id": "!roomid:example.com",
|
||||||
"status": "purging",
|
"status": "purging",
|
||||||
"shutdown_room": {
|
"shutdown_room": {
|
||||||
"kicked_users": [
|
"kicked_users": [
|
||||||
@ -842,6 +844,8 @@ A response body like the following is returned:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"status": "purging",
|
"status": "purging",
|
||||||
|
"delete_id": "bHkCNQpHqOaFhPtK",
|
||||||
|
"room_id": "!roomid:example.com",
|
||||||
"shutdown_room": {
|
"shutdown_room": {
|
||||||
"kicked_users": [
|
"kicked_users": [
|
||||||
"@foobar:example.com"
|
"@foobar:example.com"
|
||||||
@ -869,7 +873,8 @@ The following fields are returned in the JSON response body:
|
|||||||
- `results` - An array of objects, each containing information about one task.
|
- `results` - An array of objects, each containing information about one task.
|
||||||
This field is omitted from the result when you query by `delete_id`.
|
This field is omitted from the result when you query by `delete_id`.
|
||||||
Task objects contain the following fields:
|
Task objects contain the following fields:
|
||||||
- `delete_id` - The ID for this purge if you query by `room_id`.
|
- `delete_id` - The ID for this purge
|
||||||
|
- `room_id` - The ID of the room being deleted
|
||||||
- `status` - The status will be one of:
|
- `status` - The status will be one of:
|
||||||
- `shutting_down` - The process is removing users from the room.
|
- `shutting_down` - The process is removing users from the room.
|
||||||
- `purging` - The process is purging the room and event data from database.
|
- `purging` - The process is purging the room and event data from database.
|
||||||
|
54
docs/admin_api/scheduled_tasks.md
Normal file
54
docs/admin_api/scheduled_tasks.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Show scheduled tasks
|
||||||
|
|
||||||
|
This API returns information about scheduled tasks.
|
||||||
|
|
||||||
|
To use it, you will need to authenticate by providing an `access_token`
|
||||||
|
for a server admin: see [Admin API](../usage/administration/admin_api/).
|
||||||
|
|
||||||
|
The api is:
|
||||||
|
```
|
||||||
|
GET /_synapse/admin/v1/scheduled_tasks
|
||||||
|
```
|
||||||
|
|
||||||
|
It returns a JSON body like the following:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scheduled_tasks": [
|
||||||
|
{
|
||||||
|
"id": "GSA124oegf1",
|
||||||
|
"action": "shutdown_room",
|
||||||
|
"status": "complete",
|
||||||
|
"timestamp_ms": 23423523,
|
||||||
|
"resource_id": "!roomid",
|
||||||
|
"result": "some result",
|
||||||
|
"error": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Query parameters:**
|
||||||
|
|
||||||
|
* `action_name`: string - Is optional. Returns only the scheduled tasks with the given action name.
|
||||||
|
* `resource_id`: string - Is optional. Returns only the scheduled tasks with the given resource id.
|
||||||
|
* `status`: string - Is optional. Returns only the scheduled tasks matching the given status, one of
|
||||||
|
- "scheduled" - Task is scheduled but not active
|
||||||
|
- "active" - Task is active and probably running, and if not will be run on next scheduler loop run
|
||||||
|
- "complete" - Task has completed successfully
|
||||||
|
- "failed" - Task is over and either returned a failed status, or had an exception
|
||||||
|
|
||||||
|
* `max_timestamp`: int - Is optional. Returns only the scheduled tasks with a timestamp inferior to the specified one.
|
||||||
|
|
||||||
|
**Response**
|
||||||
|
|
||||||
|
The following fields are returned in the JSON response body along with a `200` HTTP status code:
|
||||||
|
|
||||||
|
* `id`: string - ID of scheduled task.
|
||||||
|
* `action`: string - The name of the scheduled task's action.
|
||||||
|
* `status`: string - The status of the scheduled task.
|
||||||
|
* `timestamp_ms`: integer - The timestamp (in milliseconds since the unix epoch) of the given task - If the status is "scheduled" then this represents when it should be launched.
|
||||||
|
Otherwise it represents the last time this task got a change of state.
|
||||||
|
* `resource_id`: Optional string - The resource id of the scheduled task, if it possesses one
|
||||||
|
* `result`: Optional Json - Any result of the scheduled task, if given
|
||||||
|
* `error`: Optional string - If the task has the status "failed", the error associated with this failure
|
@ -163,7 +163,8 @@ Body parameters:
|
|||||||
- `locked` - **bool**, optional. If unspecified, locked state will be left unchanged.
|
- `locked` - **bool**, optional. If unspecified, locked state will be left unchanged.
|
||||||
- `user_type` - **string** or null, optional. If not provided, the user type will be
|
- `user_type` - **string** or null, optional. If not provided, the user type will be
|
||||||
not be changed. If `null` is given, the user type will be cleared.
|
not be changed. If `null` is given, the user type will be cleared.
|
||||||
Other allowed options are: `bot` and `support`.
|
Other allowed options are: `bot` and `support` and any extra values defined in the homserver
|
||||||
|
[configuration](../usage/configuration/config_documentation.md#user_types).
|
||||||
|
|
||||||
## List Accounts
|
## List Accounts
|
||||||
### List Accounts (V2)
|
### List Accounts (V2)
|
||||||
@ -954,7 +955,8 @@ A response body like the following is returned:
|
|||||||
"last_seen_ip": "1.2.3.4",
|
"last_seen_ip": "1.2.3.4",
|
||||||
"last_seen_user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0",
|
"last_seen_user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0",
|
||||||
"last_seen_ts": 1474491775024,
|
"last_seen_ts": 1474491775024,
|
||||||
"user_id": "<user_id>"
|
"user_id": "<user_id>",
|
||||||
|
"dehydrated": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"device_id": "AUIECTSRND",
|
"device_id": "AUIECTSRND",
|
||||||
@ -962,7 +964,8 @@ A response body like the following is returned:
|
|||||||
"last_seen_ip": "1.2.3.5",
|
"last_seen_ip": "1.2.3.5",
|
||||||
"last_seen_user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0",
|
"last_seen_user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0",
|
||||||
"last_seen_ts": 1474491775025,
|
"last_seen_ts": 1474491775025,
|
||||||
"user_id": "<user_id>"
|
"user_id": "<user_id>",
|
||||||
|
"dehydrated": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total": 2
|
"total": 2
|
||||||
@ -992,6 +995,7 @@ The following fields are returned in the JSON response body:
|
|||||||
- `last_seen_ts` - The timestamp (in milliseconds since the unix epoch) when this
|
- `last_seen_ts` - The timestamp (in milliseconds since the unix epoch) when this
|
||||||
devices was last seen. (May be a few minutes out of date, for efficiency reasons).
|
devices was last seen. (May be a few minutes out of date, for efficiency reasons).
|
||||||
- `user_id` - Owner of device.
|
- `user_id` - Owner of device.
|
||||||
|
- `dehydrated` - Whether the device is a dehydrated device.
|
||||||
|
|
||||||
- `total` - Total number of user's devices.
|
- `total` - Total number of user's devices.
|
||||||
|
|
||||||
|
@ -150,6 +150,28 @@ $ poetry shell
|
|||||||
$ poetry install --extras all
|
$ poetry install --extras all
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you want to go even further and remove the Poetry caches:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Find your Poetry cache directory
|
||||||
|
# Docs: https://github.com/python-poetry/poetry/blob/main/docs/configuration.md#cache-directory
|
||||||
|
$ poetry config cache-dir
|
||||||
|
|
||||||
|
# Remove packages from all cached repositories
|
||||||
|
$ poetry cache clear --all .
|
||||||
|
|
||||||
|
# Go completely nuclear and clear out everything Poetry cache related
|
||||||
|
# including the wheel artifacts which is not covered by the above command
|
||||||
|
# (see https://github.com/python-poetry/poetry/issues/10304)
|
||||||
|
#
|
||||||
|
# This is necessary in order to rebuild or fetch new wheels. For example, if you update
|
||||||
|
# the `icu` library in on your system, you will need to rebuild the PyICU Python package
|
||||||
|
# in order to incorporate the correct dynamically linked library locations otherwise you
|
||||||
|
# will run into errors like: `ImportError: libicui18n.so.75: cannot open shared object file: No such file or directory`
|
||||||
|
$ rm -rf $(poetry config cache-dir)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## ...run a command in the `poetry` virtualenv?
|
## ...run a command in the `poetry` virtualenv?
|
||||||
|
|
||||||
Use `poetry run cmd args` when you need the python virtualenv context.
|
Use `poetry run cmd args` when you need the python virtualenv context.
|
||||||
@ -187,7 +209,7 @@ useful.
|
|||||||
## ...add a new dependency?
|
## ...add a new dependency?
|
||||||
|
|
||||||
Either:
|
Either:
|
||||||
- manually update `pyproject.toml`; then `poetry lock --no-update`; or else
|
- manually update `pyproject.toml`; then `poetry lock`; or else
|
||||||
- `poetry add packagename`. See `poetry add --help`; note the `--dev`,
|
- `poetry add packagename`. See `poetry add --help`; note the `--dev`,
|
||||||
`--extras` and `--optional` flags in particular.
|
`--extras` and `--optional` flags in particular.
|
||||||
|
|
||||||
@ -202,12 +224,12 @@ poetry remove packagename
|
|||||||
```
|
```
|
||||||
|
|
||||||
ought to do the trick. Alternatively, manually update `pyproject.toml` and
|
ought to do the trick. Alternatively, manually update `pyproject.toml` and
|
||||||
`poetry lock --no-update`. Include the updated `pyproject.toml` and `poetry.lock`
|
`poetry lock`. Include the updated `pyproject.toml` and `poetry.lock`
|
||||||
files in your commit.
|
files in your commit.
|
||||||
|
|
||||||
## ...update the version range for an existing dependency?
|
## ...update the version range for an existing dependency?
|
||||||
|
|
||||||
Best done by manually editing `pyproject.toml`, then `poetry lock --no-update`.
|
Best done by manually editing `pyproject.toml`, then `poetry lock`.
|
||||||
Include the updated `pyproject.toml` and `poetry.lock` in your commit.
|
Include the updated `pyproject.toml` and `poetry.lock` in your commit.
|
||||||
|
|
||||||
## ...update a dependency in the locked environment?
|
## ...update a dependency in the locked environment?
|
||||||
@ -233,7 +255,7 @@ poetry add packagename==1.2.3
|
|||||||
|
|
||||||
# Get poetry to recompute the content-hash of pyproject.toml without changing
|
# Get poetry to recompute the content-hash of pyproject.toml without changing
|
||||||
# the locked package versions.
|
# the locked package versions.
|
||||||
poetry lock --no-update
|
poetry lock
|
||||||
```
|
```
|
||||||
|
|
||||||
Either way, include the updated `poetry.lock` file in your commit.
|
Either way, include the updated `poetry.lock` file in your commit.
|
||||||
|
66
docs/modules/media_repository_callbacks.md
Normal file
66
docs/modules/media_repository_callbacks.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Media repository callbacks
|
||||||
|
|
||||||
|
Media repository callbacks allow module developers to customise the behaviour of the
|
||||||
|
media repository on a per user basis. Media repository callbacks can be registered
|
||||||
|
using the module API's `register_media_repository_callbacks` method.
|
||||||
|
|
||||||
|
The available media repository callbacks are:
|
||||||
|
|
||||||
|
### `get_media_config_for_user`
|
||||||
|
|
||||||
|
_First introduced in Synapse v1.132.0_
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def get_media_config_for_user(user_id: str) -> Optional[JsonDict]
|
||||||
|
```
|
||||||
|
|
||||||
|
**<span style="color:red">
|
||||||
|
Caution: This callback is currently experimental . The method signature or behaviour
|
||||||
|
may change without notice.
|
||||||
|
</span>**
|
||||||
|
|
||||||
|
Called when processing a request from a client for the
|
||||||
|
[media config endpoint](https://spec.matrix.org/latest/client-server-api/#get_matrixclientv1mediaconfig).
|
||||||
|
|
||||||
|
The arguments passed to this callback are:
|
||||||
|
|
||||||
|
* `user_id`: The Matrix user ID of the user (e.g. `@alice:example.com`) making the request.
|
||||||
|
|
||||||
|
If the callback returns a dictionary then it will be used as the body of the response to the
|
||||||
|
client.
|
||||||
|
|
||||||
|
If multiple modules implement this callback, they will be considered in order. If a
|
||||||
|
callback returns `None`, Synapse falls through to the next one. The value of the first
|
||||||
|
callback that does not return `None` will be used. If this happens, Synapse will not call
|
||||||
|
any of the subsequent implementations of this callback.
|
||||||
|
|
||||||
|
If no module returns a non-`None` value then the default media config will be returned.
|
||||||
|
|
||||||
|
### `is_user_allowed_to_upload_media_of_size`
|
||||||
|
|
||||||
|
_First introduced in Synapse v1.132.0_
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def is_user_allowed_to_upload_media_of_size(user_id: str, size: int) -> bool
|
||||||
|
```
|
||||||
|
|
||||||
|
**<span style="color:red">
|
||||||
|
Caution: This callback is currently experimental . The method signature or behaviour
|
||||||
|
may change without notice.
|
||||||
|
</span>**
|
||||||
|
|
||||||
|
Called before media is accepted for upload from a user, in case the module needs to
|
||||||
|
enforce a different limit for the particular user.
|
||||||
|
|
||||||
|
The arguments passed to this callback are:
|
||||||
|
|
||||||
|
* `user_id`: The Matrix user ID of the user (e.g. `@alice:example.com`) making the request.
|
||||||
|
* `size`: The size in bytes of media that is being requested to upload.
|
||||||
|
|
||||||
|
If the module returns `False`, the current request will be denied with the error code
|
||||||
|
`M_TOO_LARGE` and the HTTP status code 413.
|
||||||
|
|
||||||
|
If multiple modules implement this callback, they will be considered in order. If a callback
|
||||||
|
returns `True`, Synapse falls through to the next one. The value of the first callback that
|
||||||
|
returns `False` will be used. If this happens, Synapse will not call any of the subsequent
|
||||||
|
implementations of this callback.
|
43
docs/modules/ratelimit_callbacks.md
Normal file
43
docs/modules/ratelimit_callbacks.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Ratelimit callbacks
|
||||||
|
|
||||||
|
Ratelimit callbacks allow module developers to override ratelimit settings dynamically whilst
|
||||||
|
Synapse is running. Ratelimit callbacks can be registered using the module API's
|
||||||
|
`register_ratelimit_callbacks` method.
|
||||||
|
|
||||||
|
The available ratelimit callbacks are:
|
||||||
|
|
||||||
|
### `get_ratelimit_override_for_user`
|
||||||
|
|
||||||
|
_First introduced in Synapse v1.132.0_
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def get_ratelimit_override_for_user(user: str, limiter_name: str) -> Optional[synapse.module_api.RatelimitOverride]
|
||||||
|
```
|
||||||
|
|
||||||
|
**<span style="color:red">
|
||||||
|
Caution: This callback is currently experimental . The method signature or behaviour
|
||||||
|
may change without notice.
|
||||||
|
</span>**
|
||||||
|
|
||||||
|
Called when constructing a ratelimiter of a particular type for a user. The module can
|
||||||
|
return a `messages_per_second` and `burst_count` to be used, or `None` if
|
||||||
|
the default settings are adequate. The user is represented by their Matrix user ID
|
||||||
|
(e.g. `@alice:example.com`). The limiter name is usually taken from the `RatelimitSettings` key
|
||||||
|
value.
|
||||||
|
|
||||||
|
The limiters that are currently supported are:
|
||||||
|
|
||||||
|
- `rc_invites.per_room`
|
||||||
|
- `rc_invites.per_user`
|
||||||
|
- `rc_invites.per_issuer`
|
||||||
|
|
||||||
|
The `RatelimitOverride` return type has the following fields:
|
||||||
|
|
||||||
|
- `per_second: float`. The number of actions that can be performed in a second. `0.0` means that ratelimiting is disabled.
|
||||||
|
- `burst_count: int`. The number of actions that can be performed before being limited.
|
||||||
|
|
||||||
|
If multiple modules implement this callback, they will be considered in order. If a
|
||||||
|
callback returns `None`, Synapse falls through to the next one. The value of the first
|
||||||
|
callback that does not return `None` will be used. If this happens, Synapse will not call
|
||||||
|
any of the subsequent implementations of this callback. If no module returns a non-`None` value
|
||||||
|
then the default settings will be used.
|
@ -80,6 +80,8 @@ Called when processing an invitation, both when one is created locally or when
|
|||||||
receiving an invite over federation. Both inviter and invitee are represented by
|
receiving an invite over federation. Both inviter and invitee are represented by
|
||||||
their Matrix user ID (e.g. `@alice:example.com`).
|
their Matrix user ID (e.g. `@alice:example.com`).
|
||||||
|
|
||||||
|
Note that federated invites will call `federated_user_may_invite` before this callback.
|
||||||
|
|
||||||
|
|
||||||
The callback must return one of:
|
The callback must return one of:
|
||||||
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
|
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
|
||||||
@ -97,6 +99,34 @@ be used. If this happens, Synapse will not call any of the subsequent implementa
|
|||||||
this callback.
|
this callback.
|
||||||
|
|
||||||
|
|
||||||
|
### `federated_user_may_invite`
|
||||||
|
|
||||||
|
_First introduced in Synapse v1.133.0_
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def federated_user_may_invite(event: "synapse.events.EventBase") -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
|
||||||
|
```
|
||||||
|
|
||||||
|
Called when processing an invitation received over federation. Unlike `user_may_invite`,
|
||||||
|
this callback receives the entire event, including any stripped state in the `unsigned`
|
||||||
|
section, not just the room and user IDs.
|
||||||
|
|
||||||
|
The callback must return one of:
|
||||||
|
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
|
||||||
|
decide to reject it.
|
||||||
|
- `synapse.module_api.errors.Codes` to reject the operation with an error code. In case
|
||||||
|
of doubt, `synapse.module_api.errors.Codes.FORBIDDEN` is a good error code.
|
||||||
|
|
||||||
|
If multiple modules implement this callback, they will be considered in order. If a
|
||||||
|
callback returns `synapse.module_api.NOT_SPAM`, Synapse falls through to the next one.
|
||||||
|
The value of the first callback that does not return `synapse.module_api.NOT_SPAM` will
|
||||||
|
be used. If this happens, Synapse will not call any of the subsequent implementations of
|
||||||
|
this callback.
|
||||||
|
|
||||||
|
If all of the callbacks return `synapse.module_api.NOT_SPAM`, Synapse will also fall
|
||||||
|
through to the `user_may_invite` callback before approving the invite.
|
||||||
|
|
||||||
|
|
||||||
### `user_may_send_3pid_invite`
|
### `user_may_send_3pid_invite`
|
||||||
|
|
||||||
_First introduced in Synapse v1.45.0_
|
_First introduced in Synapse v1.45.0_
|
||||||
@ -159,12 +189,19 @@ _First introduced in Synapse v1.37.0_
|
|||||||
|
|
||||||
_Changed in Synapse v1.62.0: `synapse.module_api.NOT_SPAM` and `synapse.module_api.errors.Codes` can be returned by this callback. Returning a boolean is now deprecated._
|
_Changed in Synapse v1.62.0: `synapse.module_api.NOT_SPAM` and `synapse.module_api.errors.Codes` can be returned by this callback. Returning a boolean is now deprecated._
|
||||||
|
|
||||||
|
_Changed in Synapse v1.132.0: Added the `room_config` argument. Callbacks that only expect a single `user_id` argument are still supported._
|
||||||
|
|
||||||
```python
|
```python
|
||||||
async def user_may_create_room(user_id: str) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
|
async def user_may_create_room(user_id: str, room_config: synapse.module_api.JsonDict) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes", bool]
|
||||||
```
|
```
|
||||||
|
|
||||||
Called when processing a room creation request.
|
Called when processing a room creation request.
|
||||||
|
|
||||||
|
The arguments passed to this callback are:
|
||||||
|
|
||||||
|
* `user_id`: The Matrix user ID of the user (e.g. `@alice:example.com`).
|
||||||
|
* `room_config`: The contents of the body of a [/createRoom request](https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3createroom) as a dictionary.
|
||||||
|
|
||||||
The callback must return one of:
|
The callback must return one of:
|
||||||
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
|
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
|
||||||
decide to reject it.
|
decide to reject it.
|
||||||
@ -239,6 +276,41 @@ be used. If this happens, Synapse will not call any of the subsequent implementa
|
|||||||
this callback.
|
this callback.
|
||||||
|
|
||||||
|
|
||||||
|
### `user_may_send_state_event`
|
||||||
|
|
||||||
|
_First introduced in Synapse v1.132.0_
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def user_may_send_state_event(user_id: str, room_id: str, event_type: str, state_key: str, content: JsonDict) -> Union["synapse.module_api.NOT_SPAM", "synapse.module_api.errors.Codes"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**<span style="color:red">
|
||||||
|
Caution: This callback is currently experimental . The method signature or behaviour
|
||||||
|
may change without notice.
|
||||||
|
</span>**
|
||||||
|
|
||||||
|
Called when processing a request to [send state events](https://spec.matrix.org/latest/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey) to a room.
|
||||||
|
|
||||||
|
The arguments passed to this callback are:
|
||||||
|
|
||||||
|
* `user_id`: The Matrix user ID of the user (e.g. `@alice:example.com`) sending the state event.
|
||||||
|
* `room_id`: The ID of the room that the requested state event is being sent to.
|
||||||
|
* `event_type`: The requested type of event.
|
||||||
|
* `state_key`: The requested state key.
|
||||||
|
* `content`: The requested event contents.
|
||||||
|
|
||||||
|
The callback must return one of:
|
||||||
|
- `synapse.module_api.NOT_SPAM`, to allow the operation. Other callbacks may still
|
||||||
|
decide to reject it.
|
||||||
|
- `synapse.module_api.errors.Codes` to reject the operation with an error code. In case
|
||||||
|
of doubt, `synapse.module_api.errors.Codes.FORBIDDEN` is a good error code.
|
||||||
|
|
||||||
|
If multiple modules implement this callback, they will be considered in order. If a
|
||||||
|
callback returns `synapse.module_api.NOT_SPAM`, Synapse falls through to the next one.
|
||||||
|
The value of the first callback that does not return `synapse.module_api.NOT_SPAM` will
|
||||||
|
be used. If this happens, Synapse will not call any of the subsequent implementations of
|
||||||
|
this callback.
|
||||||
|
|
||||||
|
|
||||||
### `check_username_for_spam`
|
### `check_username_for_spam`
|
||||||
|
|
||||||
@ -353,6 +425,8 @@ callback returns `False`, Synapse falls through to the next one. The value of th
|
|||||||
callback that does not return `False` will be used. If this happens, Synapse will not call
|
callback that does not return `False` will be used. If this happens, Synapse will not call
|
||||||
any of the subsequent implementations of this callback.
|
any of the subsequent implementations of this callback.
|
||||||
|
|
||||||
|
Note that this check is applied to federation invites as of Synapse v1.130.0.
|
||||||
|
|
||||||
|
|
||||||
### `check_login_for_spam`
|
### `check_login_for_spam`
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ such as [Github][github-idp].
|
|||||||
[auth0]: https://auth0.com/
|
[auth0]: https://auth0.com/
|
||||||
[authentik]: https://goauthentik.io/
|
[authentik]: https://goauthentik.io/
|
||||||
[lemonldap]: https://lemonldap-ng.org/
|
[lemonldap]: https://lemonldap-ng.org/
|
||||||
|
[pocket-id]: https://pocket-id.org/
|
||||||
[okta]: https://www.okta.com/
|
[okta]: https://www.okta.com/
|
||||||
[dex-idp]: https://github.com/dexidp/dex
|
[dex-idp]: https://github.com/dexidp/dex
|
||||||
[keycloak-idp]: https://www.keycloak.org/docs/latest/server_admin/#sso-protocols
|
[keycloak-idp]: https://www.keycloak.org/docs/latest/server_admin/#sso-protocols
|
||||||
@ -624,6 +625,32 @@ oidc_providers:
|
|||||||
|
|
||||||
Note that the fields `client_id` and `client_secret` are taken from the CURL response above.
|
Note that the fields `client_id` and `client_secret` are taken from the CURL response above.
|
||||||
|
|
||||||
|
### Pocket ID
|
||||||
|
|
||||||
|
[Pocket ID][pocket-id] is a simple OIDC provider that allows users to authenticate with their passkeys.
|
||||||
|
1. Go to `OIDC Clients`
|
||||||
|
2. Click on `Add OIDC Client`
|
||||||
|
3. Add a name, for example `Synapse`
|
||||||
|
4. Add `"https://auth.example.org/_synapse/client/oidc/callback` to `Callback URLs` # Replace `auth.example.org` with your domain
|
||||||
|
5. Click on `Save`
|
||||||
|
6. Note down your `Client ID` and `Client secret`, these will be used later
|
||||||
|
|
||||||
|
Synapse config:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
oidc_providers:
|
||||||
|
- idp_id: pocket_id
|
||||||
|
idp_name: Pocket ID
|
||||||
|
issuer: "https://auth.example.org/" # Replace with your domain
|
||||||
|
client_id: "your-client-id" # Replace with the "Client ID" you noted down before
|
||||||
|
client_secret: "your-client-secret" # Replace with the "Client secret" you noted down before
|
||||||
|
scopes: ["openid", "profile"]
|
||||||
|
user_mapping_provider:
|
||||||
|
config:
|
||||||
|
localpart_template: "{{ user.preferred_username }}"
|
||||||
|
display_name_template: "{{ user.name }}"
|
||||||
|
```
|
||||||
|
|
||||||
### Shibboleth with OIDC Plugin
|
### Shibboleth with OIDC Plugin
|
||||||
|
|
||||||
[Shibboleth](https://www.shibboleth.net/) is an open Standard IdP solution widely used by Universities.
|
[Shibboleth](https://www.shibboleth.net/) is an open Standard IdP solution widely used by Universities.
|
||||||
|
@ -100,6 +100,14 @@ database:
|
|||||||
keepalives_count: 3
|
keepalives_count: 3
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Postgresql major version upgrades
|
||||||
|
|
||||||
|
Postgres uses separate directories for database locations between major versions (typically `/var/lib/postgresql/<version>/main`).
|
||||||
|
|
||||||
|
Therefore, it is recommended to stop Synapse and other services (MAS, etc) before upgrading Postgres major versions.
|
||||||
|
|
||||||
|
It is also strongly recommended to [back up](./usage/administration/backups.md#database) your database beforehand to ensure no data loss arising from a failed upgrade.
|
||||||
|
|
||||||
## Backups
|
## Backups
|
||||||
|
|
||||||
Don't forget to [back up](./usage/administration/backups.md#database) your database!
|
Don't forget to [back up](./usage/administration/backups.md#database) your database!
|
||||||
|
@ -5,10 +5,10 @@ It is recommended to put a reverse proxy such as
|
|||||||
[Apache](https://httpd.apache.org/docs/current/mod/mod_proxy_http.html),
|
[Apache](https://httpd.apache.org/docs/current/mod/mod_proxy_http.html),
|
||||||
[Caddy](https://caddyserver.com/docs/quick-starts/reverse-proxy),
|
[Caddy](https://caddyserver.com/docs/quick-starts/reverse-proxy),
|
||||||
[HAProxy](https://www.haproxy.org/) or
|
[HAProxy](https://www.haproxy.org/) or
|
||||||
[relayd](https://man.openbsd.org/relayd.8) in front of Synapse. One advantage
|
[relayd](https://man.openbsd.org/relayd.8) in front of Synapse.
|
||||||
of doing so is that it means that you can expose the default https port
|
This has the advantage of being able to expose the default HTTPS port (443) to Matrix
|
||||||
(443) to Matrix clients without needing to run Synapse with root
|
clients without requiring Synapse to bind to a privileged port (port numbers less than
|
||||||
privileges.
|
1024), avoiding the need for `CAP_NET_BIND_SERVICE` or running as root.
|
||||||
|
|
||||||
You should configure your reverse proxy to forward requests to `/_matrix` or
|
You should configure your reverse proxy to forward requests to `/_matrix` or
|
||||||
`/_synapse/client` to Synapse, and have it set the `X-Forwarded-For` and
|
`/_synapse/client` to Synapse, and have it set the `X-Forwarded-For` and
|
||||||
|
@ -63,7 +63,7 @@ class ExampleSpamChecker:
|
|||||||
async def user_may_invite(self, inviter_userid, invitee_userid, room_id):
|
async def user_may_invite(self, inviter_userid, invitee_userid, room_id):
|
||||||
return True # allow all invites
|
return True # allow all invites
|
||||||
|
|
||||||
async def user_may_create_room(self, userid):
|
async def user_may_create_room(self, userid, room_config):
|
||||||
return True # allow all room creations
|
return True # allow all room creations
|
||||||
|
|
||||||
async def user_may_create_room_alias(self, userid, room_alias):
|
async def user_may_create_room_alias(self, userid, room_alias):
|
||||||
|
@ -10,7 +10,7 @@ As an example, a SSO service may return the email address
|
|||||||
to turn that into a displayname when creating a Matrix user for this individual.
|
to turn that into a displayname when creating a Matrix user for this individual.
|
||||||
It may choose `John Smith`, or `Smith, John [Example.com]` or any number of
|
It may choose `John Smith`, or `Smith, John [Example.com]` or any number of
|
||||||
variations. As each Synapse configuration may want something different, this is
|
variations. As each Synapse configuration may want something different, this is
|
||||||
where SAML mapping providers come into play.
|
where SSO mapping providers come into play.
|
||||||
|
|
||||||
SSO mapping providers are currently supported for OpenID and SAML SSO
|
SSO mapping providers are currently supported for OpenID and SAML SSO
|
||||||
configurations. Please see the details below for how to implement your own.
|
configurations. Please see the details below for how to implement your own.
|
||||||
|
@ -117,6 +117,16 @@ each upgrade are complete before moving on to the next upgrade, to avoid
|
|||||||
stacking them up. You can monitor the currently running background updates with
|
stacking them up. You can monitor the currently running background updates with
|
||||||
[the Admin API](usage/administration/admin_api/background_updates.html#status).
|
[the Admin API](usage/administration/admin_api/background_updates.html#status).
|
||||||
|
|
||||||
|
# Upgrading to v1.130.0
|
||||||
|
|
||||||
|
## Documented endpoint which can be delegated to a federation worker
|
||||||
|
|
||||||
|
The endpoint `^/_matrix/federation/v1/version$` can be delegated to a federation
|
||||||
|
worker. This is not new behaviour, but had not been documented yet. The
|
||||||
|
[list of delegatable endpoints](workers.md#synapseappgeneric_worker) has
|
||||||
|
been updated to include it. Make sure to check your reverse proxy rules if you
|
||||||
|
are using workers.
|
||||||
|
|
||||||
# Upgrading to v1.126.0
|
# Upgrading to v1.126.0
|
||||||
|
|
||||||
## Room list publication rules change
|
## Room list publication rules change
|
||||||
|
@ -160,7 +160,7 @@ Using the following curl command:
|
|||||||
```console
|
```console
|
||||||
curl -H 'Authorization: Bearer <access-token>' -X DELETE https://matrix.org/_matrix/client/r0/directory/room/<room-alias>
|
curl -H 'Authorization: Bearer <access-token>' -X DELETE https://matrix.org/_matrix/client/r0/directory/room/<room-alias>
|
||||||
```
|
```
|
||||||
`<access-token>` - can be obtained in riot by looking in the riot settings, down the bottom is:
|
`<access-token>` - can be obtained in element by looking in All settings, clicking Help & About and down the bottom is:
|
||||||
Access Token:\<click to reveal\>
|
Access Token:\<click to reveal\>
|
||||||
|
|
||||||
`<room-alias>` - the room alias, eg. #my_room:matrix.org this possibly needs to be URL encoded also, for example %23my_room%3Amatrix.org
|
`<room-alias>` - the room alias, eg. #my_room:matrix.org this possibly needs to be URL encoded also, for example %23my_room%3Amatrix.org
|
||||||
@ -255,7 +255,7 @@ line to `/etc/default/matrix-synapse`:
|
|||||||
|
|
||||||
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
|
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2
|
||||||
|
|
||||||
*Note*: You may need to set `PYTHONMALLOC=malloc` to ensure that `jemalloc` can accurately calculate memory usage. By default, Python uses its internal small-object allocator, which may interfere with jemalloc's ability to track memory consumption correctly. This could prevent the [cache_autotuning](../configuration/config_documentation.md#caches-and-associated-values) feature from functioning as expected, as the Python allocator may not reach the memory threshold set by `max_cache_memory_usage`, thus not triggering the cache eviction process.
|
*Note*: You may need to set `PYTHONMALLOC=malloc` to ensure that `jemalloc` can accurately calculate memory usage. By default, Python uses its internal small-object allocator, which may interfere with jemalloc's ability to track memory consumption correctly. This could prevent the [cache_autotuning](../configuration/config_documentation.md#caches) feature from functioning as expected, as the Python allocator may not reach the memory threshold set by `max_cache_memory_usage`, thus not triggering the cache eviction process.
|
||||||
|
|
||||||
This made a significant difference on Python 2.7 - it's unclear how
|
This made a significant difference on Python 2.7 - it's unclear how
|
||||||
much of an improvement it provides on Python 3.x.
|
much of an improvement it provides on Python 3.x.
|
||||||
|
@ -30,7 +30,7 @@ The following statistics are sent to the configured reporting endpoint:
|
|||||||
| `python_version` | string | The Python version number in use (e.g "3.7.1"). Taken from `sys.version_info`. |
|
| `python_version` | string | The Python version number in use (e.g "3.7.1"). Taken from `sys.version_info`. |
|
||||||
| `total_users` | int | The number of registered users on the homeserver. |
|
| `total_users` | int | The number of registered users on the homeserver. |
|
||||||
| `total_nonbridged_users` | int | The number of users, excluding those created by an Application Service. |
|
| `total_nonbridged_users` | int | The number of users, excluding those created by an Application Service. |
|
||||||
| `daily_user_type_native` | int | The number of native users created in the last 24 hours. |
|
| `daily_user_type_native` | int | The number of native, non-guest users created in the last 24 hours. |
|
||||||
| `daily_user_type_guest` | int | The number of guest users created in the last 24 hours. |
|
| `daily_user_type_guest` | int | The number of guest users created in the last 24 hours. |
|
||||||
| `daily_user_type_bridged` | int | The number of users created by Application Services in the last 24 hours. |
|
| `daily_user_type_bridged` | int | The number of users created by Application Services in the last 24 hours. |
|
||||||
| `total_room_count` | int | The total number of rooms present on the homeserver. |
|
| `total_room_count` | int | The total number of rooms present on the homeserver. |
|
||||||
@ -50,8 +50,8 @@ The following statistics are sent to the configured reporting endpoint:
|
|||||||
| `cache_factor` | int | The configured [`global factor`](../../configuration/config_documentation.md#caching) value for caching. |
|
| `cache_factor` | int | The configured [`global factor`](../../configuration/config_documentation.md#caching) value for caching. |
|
||||||
| `event_cache_size` | int | The configured [`event_cache_size`](../../configuration/config_documentation.md#caching) value for caching. |
|
| `event_cache_size` | int | The configured [`event_cache_size`](../../configuration/config_documentation.md#caching) value for caching. |
|
||||||
| `database_engine` | string | The database engine that is in use. Either "psycopg2" meaning PostgreSQL is in use, or "sqlite3" for SQLite3. |
|
| `database_engine` | string | The database engine that is in use. Either "psycopg2" meaning PostgreSQL is in use, or "sqlite3" for SQLite3. |
|
||||||
| `database_server_version` | string | The version of the database server. Examples being "10.10" for PostgreSQL server version 10.0, and "3.38.5" for SQLite 3.38.5 installed on the system. |
|
| `database_server_version` | string | The version of the database server. Examples being "10.10" for PostgreSQL server version 10.0, and "3.38.5" for SQLite 3.38.5 installed on the system. |
|
||||||
| `log_level` | string | The log level in use. Examples are "INFO", "WARNING", "ERROR", "DEBUG", etc. |
|
| `log_level` | string | The log level in use. Examples are "INFO", "WARNING", "ERROR", "DEBUG", etc. |
|
||||||
|
|
||||||
|
|
||||||
[^1]: Native matrix users and guests are always counted. If the
|
[^1]: Native matrix users and guests are always counted. If the
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -200,6 +200,7 @@ information.
|
|||||||
^/_matrix/client/(api/v1|r0|v3)/rooms/[^/]+/initialSync$
|
^/_matrix/client/(api/v1|r0|v3)/rooms/[^/]+/initialSync$
|
||||||
|
|
||||||
# Federation requests
|
# Federation requests
|
||||||
|
^/_matrix/federation/v1/version$
|
||||||
^/_matrix/federation/v1/event/
|
^/_matrix/federation/v1/event/
|
||||||
^/_matrix/federation/v1/state/
|
^/_matrix/federation/v1/state/
|
||||||
^/_matrix/federation/v1/state_ids/
|
^/_matrix/federation/v1/state_ids/
|
||||||
@ -249,13 +250,14 @@ information.
|
|||||||
^/_matrix/client/(api/v1|r0|v3|unstable)/directory/room/.*$
|
^/_matrix/client/(api/v1|r0|v3|unstable)/directory/room/.*$
|
||||||
^/_matrix/client/(r0|v3|unstable)/capabilities$
|
^/_matrix/client/(r0|v3|unstable)/capabilities$
|
||||||
^/_matrix/client/(r0|v3|unstable)/notifications$
|
^/_matrix/client/(r0|v3|unstable)/notifications$
|
||||||
|
^/_synapse/admin/v1/rooms/
|
||||||
|
|
||||||
# Encryption requests
|
# Encryption requests
|
||||||
^/_matrix/client/(r0|v3|unstable)/keys/query$
|
^/_matrix/client/(r0|v3|unstable)/keys/query$
|
||||||
^/_matrix/client/(r0|v3|unstable)/keys/changes$
|
^/_matrix/client/(r0|v3|unstable)/keys/changes$
|
||||||
^/_matrix/client/(r0|v3|unstable)/keys/claim$
|
^/_matrix/client/(r0|v3|unstable)/keys/claim$
|
||||||
^/_matrix/client/(r0|v3|unstable)/room_keys/
|
^/_matrix/client/(r0|v3|unstable)/room_keys/
|
||||||
^/_matrix/client/(r0|v3|unstable)/keys/upload/
|
^/_matrix/client/(r0|v3|unstable)/keys/upload$
|
||||||
|
|
||||||
# Registration/login requests
|
# Registration/login requests
|
||||||
^/_matrix/client/(api/v1|r0|v3|unstable)/login$
|
^/_matrix/client/(api/v1|r0|v3|unstable)/login$
|
||||||
@ -280,6 +282,7 @@ Additionally, the following REST endpoints can be handled for GET requests:
|
|||||||
|
|
||||||
^/_matrix/client/(api/v1|r0|v3|unstable)/pushrules/
|
^/_matrix/client/(api/v1|r0|v3|unstable)/pushrules/
|
||||||
^/_matrix/client/unstable/org.matrix.msc4140/delayed_events
|
^/_matrix/client/unstable/org.matrix.msc4140/delayed_events
|
||||||
|
^/_matrix/client/(api/v1|r0|v3|unstable)/devices/
|
||||||
|
|
||||||
# Account data requests
|
# Account data requests
|
||||||
^/_matrix/client/(r0|v3|unstable)/.*/tags
|
^/_matrix/client/(r0|v3|unstable)/.*/tags
|
||||||
@ -320,6 +323,15 @@ For multiple workers not handling the SSO endpoints properly, see
|
|||||||
[#7530](https://github.com/matrix-org/synapse/issues/7530) and
|
[#7530](https://github.com/matrix-org/synapse/issues/7530) and
|
||||||
[#9427](https://github.com/matrix-org/synapse/issues/9427).
|
[#9427](https://github.com/matrix-org/synapse/issues/9427).
|
||||||
|
|
||||||
|
Additionally, when MSC3861 is enabled (`experimental_features.msc3861.enabled`
|
||||||
|
set to `true`), the following endpoints can be handled by the worker:
|
||||||
|
|
||||||
|
^/_synapse/admin/v2/users/[^/]+$
|
||||||
|
^/_synapse/admin/v1/username_available$
|
||||||
|
^/_synapse/admin/v1/users/[^/]+/_allow_cross_signing_replacement_without_uia$
|
||||||
|
# Only the GET method:
|
||||||
|
^/_synapse/admin/v1/users/[^/]+/devices$
|
||||||
|
|
||||||
Note that a [HTTP listener](usage/configuration/config_documentation.md#listeners)
|
Note that a [HTTP listener](usage/configuration/config_documentation.md#listeners)
|
||||||
with `client` and `federation` `resources` must be configured in the
|
with `client` and `federation` `resources` must be configured in the
|
||||||
[`worker_listeners`](usage/configuration/config_documentation.md#worker_listeners)
|
[`worker_listeners`](usage/configuration/config_documentation.md#worker_listeners)
|
||||||
|
1189
poetry.lock
generated
1189
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -74,6 +74,10 @@ select = [
|
|||||||
"PIE",
|
"PIE",
|
||||||
# flake8-executable
|
# flake8-executable
|
||||||
"EXE",
|
"EXE",
|
||||||
|
# flake8-logging
|
||||||
|
"LOG",
|
||||||
|
# flake8-logging-format
|
||||||
|
"G",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff.lint.isort]
|
[tool.ruff.lint.isort]
|
||||||
@ -97,7 +101,7 @@ module-name = "synapse.synapse_rust"
|
|||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "matrix-synapse"
|
name = "matrix-synapse"
|
||||||
version = "1.126.0rc1"
|
version = "1.133.0rc1"
|
||||||
description = "Homeserver for the Matrix decentralised comms protocol"
|
description = "Homeserver for the Matrix decentralised comms protocol"
|
||||||
authors = ["Matrix.org Team and Contributors <packages@matrix.org>"]
|
authors = ["Matrix.org Team and Contributors <packages@matrix.org>"]
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
@ -320,7 +324,7 @@ all = [
|
|||||||
# failing on new releases. Keeping lower bounds loose here means that dependabot
|
# failing on new releases. Keeping lower bounds loose here means that dependabot
|
||||||
# can bump versions without having to update the content-hash in the lockfile.
|
# can bump versions without having to update the content-hash in the lockfile.
|
||||||
# This helps prevents merge conflicts when running a batch of dependabot updates.
|
# This helps prevents merge conflicts when running a batch of dependabot updates.
|
||||||
ruff = "0.7.3"
|
ruff = "0.11.11"
|
||||||
# Type checking only works with the pydantic.v1 compat module from pydantic v2
|
# Type checking only works with the pydantic.v1 compat module from pydantic v2
|
||||||
pydantic = "^2"
|
pydantic = "^2"
|
||||||
|
|
||||||
@ -385,12 +389,15 @@ build-backend = "poetry.core.masonry.api"
|
|||||||
# - PyPy on Aarch64 and musllinux on aarch64: too slow to build.
|
# - PyPy on Aarch64 and musllinux on aarch64: too slow to build.
|
||||||
# c.f. https://github.com/matrix-org/synapse/pull/14259
|
# c.f. https://github.com/matrix-org/synapse/pull/14259
|
||||||
skip = "cp36* cp37* cp38* pp37* pp38* *-musllinux_i686 pp*aarch64 *-musllinux_aarch64"
|
skip = "cp36* cp37* cp38* pp37* pp38* *-musllinux_i686 pp*aarch64 *-musllinux_aarch64"
|
||||||
|
# Enable non-default builds.
|
||||||
|
# "pypy" used to be included by default up until cibuildwheel 3.
|
||||||
|
enable = "pypy"
|
||||||
|
|
||||||
# We need a rust compiler.
|
# We need a rust compiler.
|
||||||
#
|
#
|
||||||
# We temporarily pin Rust to 1.82.0 to work around
|
# We temporarily pin Rust to 1.82.0 to work around
|
||||||
# https://github.com/element-hq/synapse/issues/17988
|
# https://github.com/element-hq/synapse/issues/17988
|
||||||
before-all = "curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain 1.82.0 -y --profile minimal"
|
before-all = "sh .ci/before_build_wheel.sh"
|
||||||
environment= { PATH = "$PATH:$HOME/.cargo/bin" }
|
environment= { PATH = "$PATH:$HOME/.cargo/bin" }
|
||||||
|
|
||||||
# For some reason if we don't manually clean the build directory we
|
# For some reason if we don't manually clean the build directory we
|
||||||
|
@ -7,7 +7,7 @@ name = "synapse"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.66.0"
|
rust-version = "1.81.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "synapse"
|
name = "synapse"
|
||||||
@ -30,19 +30,27 @@ http = "1.1.0"
|
|||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
mime = "0.3.17"
|
mime = "0.3.17"
|
||||||
pyo3 = { version = "0.23.2", features = [
|
pyo3 = { version = "0.25.1", features = [
|
||||||
"macros",
|
"macros",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"abi3",
|
"abi3",
|
||||||
"abi3-py39",
|
"abi3-py39",
|
||||||
] }
|
] }
|
||||||
pyo3-log = "0.12.0"
|
pyo3-log = "0.12.4"
|
||||||
pythonize = "0.23.0"
|
pythonize = "0.25.0"
|
||||||
regex = "1.6.0"
|
regex = "1.6.0"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
serde = { version = "1.0.144", features = ["derive"] }
|
serde = { version = "1.0.144", features = ["derive"] }
|
||||||
serde_json = "1.0.85"
|
serde_json = "1.0.85"
|
||||||
ulid = "1.1.2"
|
ulid = "1.1.2"
|
||||||
|
reqwest = { version = "0.12.15", default-features = false, features = [
|
||||||
|
"http2",
|
||||||
|
"stream",
|
||||||
|
"rustls-tls-native-roots",
|
||||||
|
] }
|
||||||
|
http-body-util = "0.1.3"
|
||||||
|
futures = "0.3.31"
|
||||||
|
tokio = { version = "1.44.2", features = ["rt", "rt-multi-thread"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
extension-module = ["pyo3/extension-module"]
|
extension-module = ["pyo3/extension-module"]
|
||||||
|
@ -58,3 +58,15 @@ impl NotFoundError {
|
|||||||
NotFoundError::new_err(())
|
NotFoundError::new_err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import_exception!(synapse.api.errors, HttpResponseException);
|
||||||
|
|
||||||
|
impl HttpResponseException {
|
||||||
|
pub fn new(status: StatusCode, bytes: Vec<u8>) -> pyo3::PyErr {
|
||||||
|
HttpResponseException::new_err((
|
||||||
|
status.as_u16(),
|
||||||
|
status.canonical_reason().unwrap_or_default(),
|
||||||
|
bytes,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
218
rust/src/http_client.rs
Normal file
218
rust/src/http_client.rs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
/*
|
||||||
|
* This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 New Vector, Ltd
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* See the GNU Affero General Public License for more details:
|
||||||
|
* <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::{collections::HashMap, future::Future, panic::AssertUnwindSafe, sync::LazyLock};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use futures::{FutureExt, TryStreamExt};
|
||||||
|
use pyo3::{exceptions::PyException, prelude::*, types::PyString};
|
||||||
|
use reqwest::RequestBuilder;
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
use crate::errors::HttpResponseException;
|
||||||
|
|
||||||
|
/// The tokio runtime that we're using to run async Rust libs.
|
||||||
|
static RUNTIME: LazyLock<Runtime> = LazyLock::new(|| {
|
||||||
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.worker_threads(4)
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
/// A reference to the `Deferred` python class.
|
||||||
|
static DEFERRED_CLASS: LazyLock<PyObject> = LazyLock::new(|| {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
py.import("twisted.internet.defer")
|
||||||
|
.expect("module 'twisted.internet.defer' should be importable")
|
||||||
|
.getattr("Deferred")
|
||||||
|
.expect("module 'twisted.internet.defer' should have a 'Deferred' class")
|
||||||
|
.unbind()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
/// A reference to the twisted `reactor`.
|
||||||
|
static TWISTED_REACTOR: LazyLock<Py<PyModule>> = LazyLock::new(|| {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
py.import("twisted.internet.reactor")
|
||||||
|
.expect("module 'twisted.internet.reactor' should be importable")
|
||||||
|
.unbind()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Called when registering modules with python.
|
||||||
|
pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
|
let child_module: Bound<'_, PyModule> = PyModule::new(py, "http_client")?;
|
||||||
|
child_module.add_class::<HttpClient>()?;
|
||||||
|
|
||||||
|
// Make sure we fail early if we can't build the lazy statics.
|
||||||
|
LazyLock::force(&RUNTIME);
|
||||||
|
LazyLock::force(&DEFERRED_CLASS);
|
||||||
|
|
||||||
|
m.add_submodule(&child_module)?;
|
||||||
|
|
||||||
|
// We need to manually add the module to sys.modules to make `from
|
||||||
|
// synapse.synapse_rust import acl` work.
|
||||||
|
py.import("sys")?
|
||||||
|
.getattr("modules")?
|
||||||
|
.set_item("synapse.synapse_rust.http_client", child_module)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pyclass]
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct HttpClient {
|
||||||
|
client: reqwest::Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl HttpClient {
|
||||||
|
#[new]
|
||||||
|
pub fn py_new(user_agent: &str) -> PyResult<HttpClient> {
|
||||||
|
// The twisted reactor can only be imported after Synapse has been
|
||||||
|
// imported, to allow Synapse to change the twisted reactor. If we try
|
||||||
|
// and import the reactor too early twisted installs a default reactor,
|
||||||
|
// which can't be replaced.
|
||||||
|
LazyLock::force(&TWISTED_REACTOR);
|
||||||
|
|
||||||
|
Ok(HttpClient {
|
||||||
|
client: reqwest::Client::builder()
|
||||||
|
.user_agent(user_agent)
|
||||||
|
.build()
|
||||||
|
.context("building reqwest client")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get<'a>(
|
||||||
|
&self,
|
||||||
|
py: Python<'a>,
|
||||||
|
url: String,
|
||||||
|
response_limit: usize,
|
||||||
|
) -> PyResult<Bound<'a, PyAny>> {
|
||||||
|
self.send_request(py, self.client.get(url), response_limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post<'a>(
|
||||||
|
&self,
|
||||||
|
py: Python<'a>,
|
||||||
|
url: String,
|
||||||
|
response_limit: usize,
|
||||||
|
headers: HashMap<String, String>,
|
||||||
|
request_body: String,
|
||||||
|
) -> PyResult<Bound<'a, PyAny>> {
|
||||||
|
let mut builder = self.client.post(url);
|
||||||
|
for (name, value) in headers {
|
||||||
|
builder = builder.header(name, value);
|
||||||
|
}
|
||||||
|
builder = builder.body(request_body);
|
||||||
|
|
||||||
|
self.send_request(py, builder, response_limit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpClient {
|
||||||
|
fn send_request<'a>(
|
||||||
|
&self,
|
||||||
|
py: Python<'a>,
|
||||||
|
builder: RequestBuilder,
|
||||||
|
response_limit: usize,
|
||||||
|
) -> PyResult<Bound<'a, PyAny>> {
|
||||||
|
create_deferred(py, async move {
|
||||||
|
let response = builder.send().await.context("sending request")?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
|
||||||
|
let mut stream = response.bytes_stream();
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
while let Some(chunk) = stream.try_next().await.context("reading body")? {
|
||||||
|
if buffer.len() + chunk.len() > response_limit {
|
||||||
|
Err(anyhow::anyhow!("Response size too large"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.extend_from_slice(&chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !status.is_success() {
|
||||||
|
return Err(HttpResponseException::new(status, buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = Python::with_gil(|py| buffer.into_pyobject(py).map(|o| o.unbind()))?;
|
||||||
|
|
||||||
|
Ok(r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a twisted deferred from the given future, spawning the task on the
|
||||||
|
/// tokio runtime.
|
||||||
|
///
|
||||||
|
/// Does not handle deferred cancellation or contextvars.
|
||||||
|
fn create_deferred<F, O>(py: Python, fut: F) -> PyResult<Bound<'_, PyAny>>
|
||||||
|
where
|
||||||
|
F: Future<Output = PyResult<O>> + Send + 'static,
|
||||||
|
for<'a> O: IntoPyObject<'a>,
|
||||||
|
{
|
||||||
|
let deferred = DEFERRED_CLASS.bind(py).call0()?;
|
||||||
|
let deferred_callback = deferred.getattr("callback")?.unbind();
|
||||||
|
let deferred_errback = deferred.getattr("errback")?.unbind();
|
||||||
|
|
||||||
|
RUNTIME.spawn(async move {
|
||||||
|
// TODO: Is it safe to assert unwind safety here? I think so, as we
|
||||||
|
// don't use anything that could be tainted by the panic afterwards.
|
||||||
|
// Note that `.spawn(..)` asserts unwind safety on the future too.
|
||||||
|
let res = AssertUnwindSafe(fut).catch_unwind().await;
|
||||||
|
|
||||||
|
Python::with_gil(move |py| {
|
||||||
|
// Flatten the panic into standard python error
|
||||||
|
let res = match res {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(panic_err) => {
|
||||||
|
let panic_message = get_panic_message(&panic_err);
|
||||||
|
Err(PyException::new_err(
|
||||||
|
PyString::new(py, panic_message).unbind(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send the result to the deferred, via `.callback(..)` or `.errback(..)`
|
||||||
|
match res {
|
||||||
|
Ok(obj) => {
|
||||||
|
TWISTED_REACTOR
|
||||||
|
.call_method(py, "callFromThread", (deferred_callback, obj), None)
|
||||||
|
.expect("callFromThread should not fail"); // There's nothing we can really do with errors here
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
TWISTED_REACTOR
|
||||||
|
.call_method(py, "callFromThread", (deferred_errback, err), None)
|
||||||
|
.expect("callFromThread should not fail"); // There's nothing we can really do with errors here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(deferred)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try and get the panic message out of the panic
|
||||||
|
fn get_panic_message<'a>(panic_err: &'a (dyn std::any::Any + Send + 'static)) -> &'a str {
|
||||||
|
// Apparently this is how you extract the panic message from a panic
|
||||||
|
if let Some(str_slice) = panic_err.downcast_ref::<&str>() {
|
||||||
|
str_slice
|
||||||
|
} else if let Some(string) = panic_err.downcast_ref::<String>() {
|
||||||
|
string
|
||||||
|
} else {
|
||||||
|
"unknown error"
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ pub enum IdentifierError {
|
|||||||
|
|
||||||
impl fmt::Display for IdentifierError {
|
impl fmt::Display for IdentifierError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{:?}", self)
|
write!(f, "{self:?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ pub mod acl;
|
|||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
|
pub mod http_client;
|
||||||
pub mod identifier;
|
pub mod identifier;
|
||||||
pub mod matrix_const;
|
pub mod matrix_const;
|
||||||
pub mod push;
|
pub mod push;
|
||||||
@ -50,6 +51,7 @@ fn synapse_rust(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|||||||
acl::register_module(py, m)?;
|
acl::register_module(py, m)?;
|
||||||
push::register_module(py, m)?;
|
push::register_module(py, m)?;
|
||||||
events::register_module(py, m)?;
|
events::register_module(py, m)?;
|
||||||
|
http_client::register_module(py, m)?;
|
||||||
rendezvous::register_module(py, m)?;
|
rendezvous::register_module(py, m)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
5746
schema/synapse-config.schema.yaml
Normal file
5746
schema/synapse-config.schema.yaml
Normal file
File diff suppressed because it is too large
Load Diff
2
schema/v1/Do not edit files in this folder
Normal file
2
schema/v1/Do not edit files in this folder
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
If you want to update the meta schema, copy this folder and increase its version
|
||||||
|
number instead.
|
29
schema/v1/meta.schema.json
Normal file
29
schema/v1/meta.schema.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://element-hq.github.io/synapse/latest/schema/v1/meta.schema.json",
|
||||||
|
"$vocabulary": {
|
||||||
|
"https://json-schema.org/draft/2020-12/vocab/core": true,
|
||||||
|
"https://json-schema.org/draft/2020-12/vocab/applicator": true,
|
||||||
|
"https://json-schema.org/draft/2020-12/vocab/unevaluated": true,
|
||||||
|
"https://json-schema.org/draft/2020-12/vocab/validation": true,
|
||||||
|
"https://json-schema.org/draft/2020-12/vocab/meta-data": true,
|
||||||
|
"https://json-schema.org/draft/2020-12/vocab/format-annotation": true,
|
||||||
|
"https://json-schema.org/draft/2020-12/vocab/content": true,
|
||||||
|
"https://element-hq.github.io/synapse/latest/schema/v1/vocab/documentation": false
|
||||||
|
},
|
||||||
|
"$ref": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"properties": {
|
||||||
|
"io.element.type_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Human-readable type of a schema that is displayed instead of the standard JSON Schema types like `object` or `integer`. In case the JSON Schema type contains `null`, this information should be presented alongside the human-readable type name.",
|
||||||
|
"examples": ["duration", "byte size"]
|
||||||
|
},
|
||||||
|
"io.element.post_description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Additional description of a schema, better suited to be placed less prominently in the generated documentation, e.g., at the end of a section after listings of items and properties.",
|
||||||
|
"examples": [
|
||||||
|
"### Advanced uses\n\nThe spent coffee grounds can be added to compost for improving soil and growing plants."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
schema/v1/vocab/documentation.html
Normal file
11
schema/v1/vocab/documentation.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="refresh" content="0; URL=../meta.schema.json">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Redirecting to ../meta.schema.json…</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Redirecting to <a href="../meta.schema.json">../meta.schema.json</a>…</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -243,7 +243,7 @@ def do_lint() -> Set[str]:
|
|||||||
importlib.import_module(module_info.name)
|
importlib.import_module(module_info.name)
|
||||||
except ModelCheckerException as e:
|
except ModelCheckerException as e:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Bad annotation found when importing {module_info.name}"
|
"Bad annotation found when importing %s", module_info.name
|
||||||
)
|
)
|
||||||
failures.add(format_model_checker_exception(e))
|
failures.add(format_model_checker_exception(e))
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Check that no schema deltas have been added to the wrong version.
|
# Check that no schema deltas have been added to the wrong version.
|
||||||
|
#
|
||||||
|
# Also checks that schema deltas do not try and create or drop indices.
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
@ -9,6 +11,13 @@ import click
|
|||||||
import git
|
import git
|
||||||
|
|
||||||
SCHEMA_FILE_REGEX = re.compile(r"^synapse/storage/schema/(.*)/delta/(.*)/(.*)$")
|
SCHEMA_FILE_REGEX = re.compile(r"^synapse/storage/schema/(.*)/delta/(.*)/(.*)$")
|
||||||
|
INDEX_CREATION_REGEX = re.compile(r"CREATE .*INDEX .*ON ([a-z_]+)", flags=re.IGNORECASE)
|
||||||
|
INDEX_DELETION_REGEX = re.compile(r"DROP .*INDEX ([a-z_]+)", flags=re.IGNORECASE)
|
||||||
|
TABLE_CREATION_REGEX = re.compile(r"CREATE .*TABLE ([a-z_]+)", flags=re.IGNORECASE)
|
||||||
|
|
||||||
|
# The base branch we want to check against. We use the main development branch
|
||||||
|
# on the assumption that is what we are developing against.
|
||||||
|
DEVELOP_BRANCH = "develop"
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
@ -20,6 +29,9 @@ SCHEMA_FILE_REGEX = re.compile(r"^synapse/storage/schema/(.*)/delta/(.*)/(.*)$")
|
|||||||
help="Always output ANSI colours",
|
help="Always output ANSI colours",
|
||||||
)
|
)
|
||||||
def main(force_colors: bool) -> None:
|
def main(force_colors: bool) -> None:
|
||||||
|
# Return code. Set to non-zero when we encounter an error
|
||||||
|
return_code = 0
|
||||||
|
|
||||||
click.secho(
|
click.secho(
|
||||||
"+++ Checking schema deltas are in the right folder",
|
"+++ Checking schema deltas are in the right folder",
|
||||||
fg="green",
|
fg="green",
|
||||||
@ -30,17 +42,17 @@ def main(force_colors: bool) -> None:
|
|||||||
click.secho("Updating repo...")
|
click.secho("Updating repo...")
|
||||||
|
|
||||||
repo = git.Repo()
|
repo = git.Repo()
|
||||||
repo.remote().fetch()
|
repo.remote().fetch(refspec=DEVELOP_BRANCH)
|
||||||
|
|
||||||
click.secho("Getting current schema version...")
|
click.secho("Getting current schema version...")
|
||||||
|
|
||||||
r = repo.git.show("origin/develop:synapse/storage/schema/__init__.py")
|
r = repo.git.show(f"origin/{DEVELOP_BRANCH}:synapse/storage/schema/__init__.py")
|
||||||
|
|
||||||
locals: Dict[str, Any] = {}
|
locals: Dict[str, Any] = {}
|
||||||
exec(r, locals)
|
exec(r, locals)
|
||||||
current_schema_version = locals["SCHEMA_VERSION"]
|
current_schema_version = locals["SCHEMA_VERSION"]
|
||||||
|
|
||||||
diffs: List[git.Diff] = repo.remote().refs.develop.commit.diff(None)
|
diffs: List[git.Diff] = repo.remote().refs[DEVELOP_BRANCH].commit.diff(None)
|
||||||
|
|
||||||
# Get the schema version of the local file to check against current schema on develop
|
# Get the schema version of the local file to check against current schema on develop
|
||||||
with open("synapse/storage/schema/__init__.py") as file:
|
with open("synapse/storage/schema/__init__.py") as file:
|
||||||
@ -53,7 +65,7 @@ def main(force_colors: bool) -> None:
|
|||||||
# local schema version must be +/-1 the current schema version on develop
|
# local schema version must be +/-1 the current schema version on develop
|
||||||
if abs(local_schema_version - current_schema_version) != 1:
|
if abs(local_schema_version - current_schema_version) != 1:
|
||||||
click.secho(
|
click.secho(
|
||||||
"The proposed schema version has diverged more than one version from develop, please fix!",
|
f"The proposed schema version has diverged more than one version from {DEVELOP_BRANCH}, please fix!",
|
||||||
fg="red",
|
fg="red",
|
||||||
bold=True,
|
bold=True,
|
||||||
color=force_colors,
|
color=force_colors,
|
||||||
@ -67,21 +79,28 @@ def main(force_colors: bool) -> None:
|
|||||||
click.secho(f"Current schema version: {current_schema_version}")
|
click.secho(f"Current schema version: {current_schema_version}")
|
||||||
|
|
||||||
seen_deltas = False
|
seen_deltas = False
|
||||||
bad_files = []
|
bad_delta_files = []
|
||||||
|
changed_delta_files = []
|
||||||
for diff in diffs:
|
for diff in diffs:
|
||||||
if not diff.new_file or diff.b_path is None:
|
if diff.b_path is None:
|
||||||
|
# We don't lint deleted files.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match = SCHEMA_FILE_REGEX.match(diff.b_path)
|
match = SCHEMA_FILE_REGEX.match(diff.b_path)
|
||||||
if not match:
|
if not match:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
changed_delta_files.append(diff.b_path)
|
||||||
|
|
||||||
|
if not diff.new_file:
|
||||||
|
continue
|
||||||
|
|
||||||
seen_deltas = True
|
seen_deltas = True
|
||||||
|
|
||||||
_, delta_version, _ = match.groups()
|
_, delta_version, _ = match.groups()
|
||||||
|
|
||||||
if delta_version != str(current_schema_version):
|
if delta_version != str(current_schema_version):
|
||||||
bad_files.append(diff.b_path)
|
bad_delta_files.append(diff.b_path)
|
||||||
|
|
||||||
if not seen_deltas:
|
if not seen_deltas:
|
||||||
click.secho(
|
click.secho(
|
||||||
@ -92,41 +111,91 @@ def main(force_colors: bool) -> None:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not bad_files:
|
if bad_delta_files:
|
||||||
|
bad_delta_files.sort()
|
||||||
|
|
||||||
|
click.secho(
|
||||||
|
"Found deltas in the wrong folder!",
|
||||||
|
fg="red",
|
||||||
|
bold=True,
|
||||||
|
color=force_colors,
|
||||||
|
)
|
||||||
|
|
||||||
|
for f in bad_delta_files:
|
||||||
|
click.secho(
|
||||||
|
f"\t{f}",
|
||||||
|
fg="red",
|
||||||
|
bold=True,
|
||||||
|
color=force_colors,
|
||||||
|
)
|
||||||
|
|
||||||
|
click.secho()
|
||||||
|
click.secho(
|
||||||
|
f"Please move these files to delta/{current_schema_version}/",
|
||||||
|
fg="red",
|
||||||
|
bold=True,
|
||||||
|
color=force_colors,
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
click.secho(
|
click.secho(
|
||||||
f"All deltas are in the correct folder: {current_schema_version}!",
|
f"All deltas are in the correct folder: {current_schema_version}!",
|
||||||
fg="green",
|
fg="green",
|
||||||
bold=True,
|
bold=True,
|
||||||
color=force_colors,
|
color=force_colors,
|
||||||
)
|
)
|
||||||
return
|
|
||||||
|
|
||||||
bad_files.sort()
|
# Make sure we process them in order. This sort works because deltas are numbered
|
||||||
|
# and delta files are also numbered in order.
|
||||||
|
changed_delta_files.sort()
|
||||||
|
|
||||||
click.secho(
|
# Now check that we're not trying to create or drop indices. If we want to
|
||||||
"Found deltas in the wrong folder!",
|
# do that they should be in background updates. The exception is when we
|
||||||
fg="red",
|
# create indices on tables we've just created.
|
||||||
bold=True,
|
created_tables = set()
|
||||||
color=force_colors,
|
for delta_file in changed_delta_files:
|
||||||
)
|
with open(delta_file) as fd:
|
||||||
|
delta_lines = fd.readlines()
|
||||||
|
|
||||||
for f in bad_files:
|
for line in delta_lines:
|
||||||
click.secho(
|
# Strip SQL comments
|
||||||
f"\t{f}",
|
line = line.split("--", maxsplit=1)[0]
|
||||||
fg="red",
|
|
||||||
bold=True,
|
|
||||||
color=force_colors,
|
|
||||||
)
|
|
||||||
|
|
||||||
click.secho()
|
# Check and track any tables we create
|
||||||
click.secho(
|
match = TABLE_CREATION_REGEX.search(line)
|
||||||
f"Please move these files to delta/{current_schema_version}/",
|
if match:
|
||||||
fg="red",
|
table_name = match.group(1)
|
||||||
bold=True,
|
created_tables.add(table_name)
|
||||||
color=force_colors,
|
|
||||||
)
|
|
||||||
|
|
||||||
click.get_current_context().exit(1)
|
# Check for dropping indices, these are always banned
|
||||||
|
match = INDEX_DELETION_REGEX.search(line)
|
||||||
|
if match:
|
||||||
|
clause = match.group()
|
||||||
|
|
||||||
|
click.secho(
|
||||||
|
f"Found delta with index deletion: '{clause}' in {delta_file}\nThese should be in background updates.",
|
||||||
|
fg="red",
|
||||||
|
bold=True,
|
||||||
|
color=force_colors,
|
||||||
|
)
|
||||||
|
return_code = 1
|
||||||
|
|
||||||
|
# Check for index creation, which is only allowed for tables we've
|
||||||
|
# created.
|
||||||
|
match = INDEX_CREATION_REGEX.search(line)
|
||||||
|
if match:
|
||||||
|
clause = match.group()
|
||||||
|
table_name = match.group(1)
|
||||||
|
if table_name not in created_tables:
|
||||||
|
click.secho(
|
||||||
|
f"Found delta with index creation: '{clause}' in {delta_file}\nThese should be in background updates.",
|
||||||
|
fg="red",
|
||||||
|
bold=True,
|
||||||
|
color=force_colors,
|
||||||
|
)
|
||||||
|
return_code = 1
|
||||||
|
|
||||||
|
click.get_current_context().exit(return_code)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -229,6 +229,7 @@ test_packages=(
|
|||||||
./tests/msc3902
|
./tests/msc3902
|
||||||
./tests/msc3967
|
./tests/msc3967
|
||||||
./tests/msc4140
|
./tests/msc4140
|
||||||
|
./tests/msc4155
|
||||||
)
|
)
|
||||||
|
|
||||||
# Enable dirty runs, so tests will reuse the same container where possible.
|
# Enable dirty runs, so tests will reuse the same container where possible.
|
||||||
|
503
scripts-dev/gen_config_documentation.py
Executable file
503
scripts-dev/gen_config_documentation.py
Executable file
@ -0,0 +1,503 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate Synapse documentation from JSON Schema file."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
HEADER = """<!-- Document auto-generated by scripts-dev/gen_config_documentation.py -->
|
||||||
|
|
||||||
|
# Configuring Synapse
|
||||||
|
|
||||||
|
This is intended as a guide to the Synapse configuration. The behavior of a Synapse instance can be modified
|
||||||
|
through the many configuration settings documented here — each config option is explained,
|
||||||
|
including what the default is, how to change the default and what sort of behaviour the setting governs.
|
||||||
|
Also included is an example configuration for each setting. If you don't want to spend a lot of time
|
||||||
|
thinking about options, the config as generated sets sensible defaults for all values. Do note however that the
|
||||||
|
database defaults to SQLite, which is not recommended for production usage. You can read more on this subject
|
||||||
|
[here](../../setup/installation.md#using-postgresql).
|
||||||
|
|
||||||
|
## Config Conventions
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
In addition, configuration options referring to size use the following suffixes:
|
||||||
|
|
||||||
|
* `K` = KiB, or 1024 bytes
|
||||||
|
* `M` = MiB, or 1,048,576 bytes
|
||||||
|
* `G` = GiB, or 1,073,741,824 bytes
|
||||||
|
* `T` = TiB, or 1,099,511,627,776 bytes
|
||||||
|
|
||||||
|
For example, setting `max_avatar_size: 10M` means that Synapse will not accept files larger than 10,485,760 bytes
|
||||||
|
for a user avatar.
|
||||||
|
|
||||||
|
## Config Validation
|
||||||
|
|
||||||
|
The configuration file can be validated with the following command:
|
||||||
|
```bash
|
||||||
|
python -m synapse.config read <config key to print> -c <path to config>
|
||||||
|
```
|
||||||
|
|
||||||
|
To validate the entire file, omit `read <config key to print>`:
|
||||||
|
```bash
|
||||||
|
python -m synapse.config -c <path to config>
|
||||||
|
```
|
||||||
|
|
||||||
|
To see how to set other options, check the help reference:
|
||||||
|
```bash
|
||||||
|
python -m synapse.config --help
|
||||||
|
```
|
||||||
|
|
||||||
|
### YAML
|
||||||
|
The configuration file is a [YAML](https://yaml.org/) file, which means that certain syntax rules
|
||||||
|
apply if you want your config file to be read properly. A few helpful things to know:
|
||||||
|
* `#` before any option in the config will comment out that setting and either a default (if available) will
|
||||||
|
be applied or Synapse will ignore the setting. Thus, in example #1 below, the setting will be read and
|
||||||
|
applied, but in example #2 the setting will not be read and a default will be applied.
|
||||||
|
|
||||||
|
Example #1:
|
||||||
|
```yaml
|
||||||
|
pid_file: DATADIR/homeserver.pid
|
||||||
|
```
|
||||||
|
Example #2:
|
||||||
|
```yaml
|
||||||
|
#pid_file: DATADIR/homeserver.pid
|
||||||
|
```
|
||||||
|
* Indentation matters! The indentation before a setting
|
||||||
|
will determine whether a given setting is read as part of another
|
||||||
|
setting, or considered on its own. Thus, in example #1, the `enabled` setting
|
||||||
|
is read as a sub-option of the `presence` setting, and will be properly applied.
|
||||||
|
|
||||||
|
However, the lack of indentation before the `enabled` setting in example #2 means
|
||||||
|
that when reading the config, Synapse will consider both `presence` and `enabled` as
|
||||||
|
different settings. In this case, `presence` has no value, and thus a default applied, and `enabled`
|
||||||
|
is an option that Synapse doesn't recognize and thus ignores.
|
||||||
|
|
||||||
|
Example #1:
|
||||||
|
```yaml
|
||||||
|
presence:
|
||||||
|
enabled: false
|
||||||
|
```
|
||||||
|
Example #2:
|
||||||
|
```yaml
|
||||||
|
presence:
|
||||||
|
enabled: false
|
||||||
|
```
|
||||||
|
In this manual, all top-level settings (ones with no indentation) are identified
|
||||||
|
at the beginning of their section (i.e. "### `example_setting`") and
|
||||||
|
the sub-options, if any, are identified and listed in the body of the section.
|
||||||
|
In addition, each setting has an example of its usage, with the proper indentation
|
||||||
|
shown.
|
||||||
|
"""
|
||||||
|
SECTION_HEADERS = {
|
||||||
|
"modules": {
|
||||||
|
"title": "Modules",
|
||||||
|
"description": (
|
||||||
|
"Server admins can expand Synapse's functionality with external "
|
||||||
|
"modules.\n\n"
|
||||||
|
"See [here](../../modules/index.md) for more documentation on how "
|
||||||
|
"to configure or create custom modules for Synapse."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"server_name": {
|
||||||
|
"title": "Server",
|
||||||
|
"description": "Define your homeserver name and other base options.",
|
||||||
|
},
|
||||||
|
"admin_contact": {
|
||||||
|
"title": "Homeserver blocking",
|
||||||
|
"description": "Useful options for Synapse admins.",
|
||||||
|
},
|
||||||
|
"tls_certificate_path": {
|
||||||
|
"title": "TLS",
|
||||||
|
"description": "Options related to TLS.",
|
||||||
|
},
|
||||||
|
"federation_domain_whitelist": {
|
||||||
|
"title": "Federation",
|
||||||
|
"description": "Options related to federation.",
|
||||||
|
},
|
||||||
|
"event_cache_size": {
|
||||||
|
"title": "Caching",
|
||||||
|
"description": "Options related to caching.",
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"title": "Database",
|
||||||
|
"description": "Config options related to database settings.",
|
||||||
|
},
|
||||||
|
"log_config": {
|
||||||
|
"title": "Logging",
|
||||||
|
"description": ("Config options related to logging."),
|
||||||
|
},
|
||||||
|
"rc_message": {
|
||||||
|
"title": "Ratelimiting",
|
||||||
|
"description": (
|
||||||
|
"Options related to ratelimiting in Synapse.\n\n"
|
||||||
|
"Each ratelimiting configuration is made of two parameters:\n"
|
||||||
|
"- `per_second`: number of requests a client can send per second.\n"
|
||||||
|
"- `burst_count`: number of requests a client can send before "
|
||||||
|
"being throttled."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"enable_authenticated_media": {
|
||||||
|
"title": "Media Store",
|
||||||
|
"description": "Config options related to Synapse's media store.",
|
||||||
|
},
|
||||||
|
"recaptcha_public_key": {
|
||||||
|
"title": "Captcha",
|
||||||
|
"description": (
|
||||||
|
"See [here](../../CAPTCHA_SETUP.md) for full details on setting up captcha."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"turn_uris": {
|
||||||
|
"title": "TURN",
|
||||||
|
"description": ("Options related to adding a TURN server to Synapse."),
|
||||||
|
},
|
||||||
|
"enable_registration": {
|
||||||
|
"title": "Registration",
|
||||||
|
"description": (
|
||||||
|
"Registration can be rate-limited using the parameters in the "
|
||||||
|
"[Ratelimiting](#ratelimiting) section of this manual."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"session_lifetime": {
|
||||||
|
"title": "User session management",
|
||||||
|
"description": ("Config options related to user session management."),
|
||||||
|
},
|
||||||
|
"enable_metrics": {
|
||||||
|
"title": "Metrics",
|
||||||
|
"description": ("Config options related to metrics."),
|
||||||
|
},
|
||||||
|
"room_prejoin_state": {
|
||||||
|
"title": "API Configuration",
|
||||||
|
"description": ("Config settings related to the client/server API."),
|
||||||
|
},
|
||||||
|
"signing_key_path": {
|
||||||
|
"title": "Signing Keys",
|
||||||
|
"description": ("Config options relating to signing keys."),
|
||||||
|
},
|
||||||
|
"saml2_config": {
|
||||||
|
"title": "Single sign-on integration",
|
||||||
|
"description": (
|
||||||
|
"The following settings can be used to make Synapse use a single sign-on provider for authentication, instead of its internal password database.\n\n"
|
||||||
|
"You will probably also want to set the following options to `false` to disable the regular login/registration flows:\n"
|
||||||
|
"* [`enable_registration`](#enable_registration)\n"
|
||||||
|
"* [`password_config.enabled`](#password_config)"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"push": {
|
||||||
|
"title": "Push",
|
||||||
|
"description": ("Configuration settings related to push notifications."),
|
||||||
|
},
|
||||||
|
"encryption_enabled_by_default_for_room_type": {
|
||||||
|
"title": "Rooms",
|
||||||
|
"description": ("Config options relating to rooms."),
|
||||||
|
},
|
||||||
|
"opentracing": {
|
||||||
|
"title": "Opentracing",
|
||||||
|
"description": ("Configuration options related to Opentracing support."),
|
||||||
|
},
|
||||||
|
"worker_replication_secret": {
|
||||||
|
"title": "Coordinating workers",
|
||||||
|
"description": (
|
||||||
|
"Configuration options related to workers which belong in the main config file (usually called `homeserver.yaml`). A Synapse deployment can scale horizontally by running multiple Synapse processes called _workers_. Incoming requests are distributed between workers to handle higher loads. Some workers are privileged and can accept requests from other workers.\n\n"
|
||||||
|
"As a result, the worker configuration is divided into two parts.\n\n"
|
||||||
|
"1. The first part (in this section of the manual) defines which shardable tasks are delegated to privileged workers. This allows unprivileged workers to make requests to a privileged worker to act on their behalf.\n"
|
||||||
|
"2. [The second part](#individual-worker-configuration) controls the behaviour of individual workers in isolation.\n\n"
|
||||||
|
"For guidance on setting up workers, see the [worker documentation](../../workers.md)."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"worker_app": {
|
||||||
|
"title": "Individual worker configuration",
|
||||||
|
"description": (
|
||||||
|
"These options configure an individual worker, in its worker configuration file. They should be not be provided when configuring the main process.\n\n"
|
||||||
|
"Note also the configuration above for [coordinating a cluster of workers](#coordinating-workers).\n\n"
|
||||||
|
"For guidance on setting up workers, see the [worker documentation](../../workers.md)."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"background_updates": {
|
||||||
|
"title": "Background Updates",
|
||||||
|
"description": ("Configuration settings related to background updates."),
|
||||||
|
},
|
||||||
|
"auto_accept_invites": {
|
||||||
|
"title": "Auto Accept Invites",
|
||||||
|
"description": (
|
||||||
|
"Configuration settings related to automatically accepting invites."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
INDENT = " "
|
||||||
|
|
||||||
|
|
||||||
|
has_error = False
|
||||||
|
|
||||||
|
|
||||||
|
def error(text: str) -> None:
|
||||||
|
global has_error
|
||||||
|
print(f"ERROR: {text}", file=sys.stderr)
|
||||||
|
has_error = True
|
||||||
|
|
||||||
|
|
||||||
|
def indent(text: str, first_line: bool = True) -> str:
|
||||||
|
"""Indents each non-empty line of the given text."""
|
||||||
|
text = re.sub(r"(\n)([^\n])", r"\1" + INDENT + r"\2", text)
|
||||||
|
if first_line:
|
||||||
|
text = re.sub(r"^([^\n])", INDENT + r"\1", text)
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def em(s: Optional[str]) -> str:
|
||||||
|
"""Add emphasis to text."""
|
||||||
|
return f"*{s}*" if s else ""
|
||||||
|
|
||||||
|
|
||||||
|
def a(s: Optional[str], suffix: str = " ") -> str:
|
||||||
|
"""Appends a space if the given string is not empty."""
|
||||||
|
return s + suffix if s else ""
|
||||||
|
|
||||||
|
|
||||||
|
def p(s: Optional[str], prefix: str = " ") -> str:
|
||||||
|
"""Prepend a space if the given string is not empty."""
|
||||||
|
return prefix + s if s else ""
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_local_refs(schema: dict) -> dict:
|
||||||
|
"""Returns the given schema with local $ref properties replaced by their keywords.
|
||||||
|
|
||||||
|
Crude approximation that will override keywords.
|
||||||
|
"""
|
||||||
|
defs = schema["$defs"]
|
||||||
|
|
||||||
|
def replace_ref(d: Any) -> Any:
|
||||||
|
if isinstance(d, dict):
|
||||||
|
the_def = {}
|
||||||
|
if "$ref" in d:
|
||||||
|
# Found a "$ref" key.
|
||||||
|
def_name = d["$ref"].removeprefix("#/$defs/")
|
||||||
|
del d["$ref"]
|
||||||
|
the_def = defs[def_name]
|
||||||
|
|
||||||
|
new_dict = {k: replace_ref(v) for k, v in d.items()}
|
||||||
|
if common_keys := (new_dict.keys() & the_def.keys()) - {"properties"}:
|
||||||
|
print(
|
||||||
|
f"WARN: '{def_name}' overrides keys '{common_keys}'",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
|
new_dict_props = new_dict.get("properties", {})
|
||||||
|
the_def_props = the_def.get("properties", {})
|
||||||
|
if common_props := new_dict_props.keys() & the_def_props.keys():
|
||||||
|
print(
|
||||||
|
f"WARN: '{def_name}' overrides properties '{common_props}'",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
if merged_props := {**new_dict_props, **the_def_props}:
|
||||||
|
return {**new_dict, **the_def, "properties": merged_props}
|
||||||
|
else:
|
||||||
|
return {**new_dict, **the_def}
|
||||||
|
|
||||||
|
elif isinstance(d, list):
|
||||||
|
return [replace_ref(v) for v in d]
|
||||||
|
else:
|
||||||
|
return d
|
||||||
|
|
||||||
|
return replace_ref(schema)
|
||||||
|
|
||||||
|
|
||||||
|
def sep(values: dict) -> str:
|
||||||
|
"""Separator between parts of the description."""
|
||||||
|
# If description is multiple paragraphs already, add new ones. Otherwise
|
||||||
|
# append to same paragraph.
|
||||||
|
return "\n\n" if "\n\n" in values.get("description", "") else " "
|
||||||
|
|
||||||
|
|
||||||
|
def type_str(values: dict) -> str:
|
||||||
|
"""Type of the current value."""
|
||||||
|
if t := values.get("io.element.type_name"):
|
||||||
|
# Allow custom overrides for the type name, for documentation clarity
|
||||||
|
return f"({t})"
|
||||||
|
if not (t := values.get("type")):
|
||||||
|
return ""
|
||||||
|
if not isinstance(t, list):
|
||||||
|
t = [t]
|
||||||
|
joined = "|".join(t)
|
||||||
|
return f"({joined})"
|
||||||
|
|
||||||
|
|
||||||
|
def items(values: dict) -> str:
|
||||||
|
"""A block listing properties of array items."""
|
||||||
|
if not (items := values.get("items")):
|
||||||
|
return ""
|
||||||
|
if not (item_props := items.get("properties")):
|
||||||
|
return ""
|
||||||
|
return "\nOptions for each entry include:\n\n" + "\n".join(
|
||||||
|
sub_section(k, v) for k, v in item_props.items()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def properties(values: dict) -> str:
|
||||||
|
"""A block listing object properties."""
|
||||||
|
if not (properties := values.get("properties")):
|
||||||
|
return ""
|
||||||
|
return "\nThis setting has the following sub-options:\n\n" + "\n".join(
|
||||||
|
sub_section(k, v) for k, v in properties.items()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sub_section(prop: str, values: dict) -> str:
|
||||||
|
"""Formats a bullet point about the given sub-property."""
|
||||||
|
sep = lambda: globals()["sep"](values)
|
||||||
|
type_str = lambda: globals()["type_str"](values)
|
||||||
|
items = lambda: globals()["items"](values)
|
||||||
|
properties = lambda: globals()["properties"](values)
|
||||||
|
|
||||||
|
def default() -> str:
|
||||||
|
try:
|
||||||
|
default = values["default"]
|
||||||
|
return f"Defaults to `{json.dumps(default)}`."
|
||||||
|
except KeyError:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def description() -> str:
|
||||||
|
if not (description := values.get("description")):
|
||||||
|
error(f"missing description for {prop}")
|
||||||
|
return "MISSING DESCRIPTION\n"
|
||||||
|
|
||||||
|
return f"{description}{p(default(), sep())}\n"
|
||||||
|
|
||||||
|
return (
|
||||||
|
f"* `{prop}`{p(type_str())}: "
|
||||||
|
+ f"{indent(description(), first_line=False)}"
|
||||||
|
+ indent(items())
|
||||||
|
+ indent(properties())
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def section(prop: str, values: dict) -> str:
|
||||||
|
"""Formats a section about the given property."""
|
||||||
|
sep = lambda: globals()["sep"](values)
|
||||||
|
type_str = lambda: globals()["type_str"](values)
|
||||||
|
items = lambda: globals()["items"](values)
|
||||||
|
properties = lambda: globals()["properties"](values)
|
||||||
|
|
||||||
|
def is_simple_default() -> bool:
|
||||||
|
"""Whether the given default is simple enough for a one-liner."""
|
||||||
|
if not (d := values.get("default")):
|
||||||
|
return True
|
||||||
|
return not isinstance(d, dict) and not isinstance(d, list)
|
||||||
|
|
||||||
|
def default_str() -> str:
|
||||||
|
try:
|
||||||
|
default = values["default"]
|
||||||
|
except KeyError:
|
||||||
|
t = values.get("type", [])
|
||||||
|
if "object" == t or "object" in t:
|
||||||
|
# Skip objects as they probably have child defaults.
|
||||||
|
return ""
|
||||||
|
return "There is no default for this option."
|
||||||
|
|
||||||
|
if not is_simple_default():
|
||||||
|
# Show complex defaults as a code block instead.
|
||||||
|
return ""
|
||||||
|
return f"Defaults to `{json.dumps(default)}`."
|
||||||
|
|
||||||
|
def header() -> str:
|
||||||
|
try:
|
||||||
|
title = SECTION_HEADERS[prop]["title"]
|
||||||
|
description = SECTION_HEADERS[prop]["description"]
|
||||||
|
return f"## {title}\n\n{description}\n\n---\n"
|
||||||
|
except KeyError:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def title() -> str:
|
||||||
|
return f"### `{prop}`\n"
|
||||||
|
|
||||||
|
def description() -> str:
|
||||||
|
if not (description := values.get("description")):
|
||||||
|
error(f"missing description for {prop}")
|
||||||
|
return "MISSING DESCRIPTION\n"
|
||||||
|
return f"\n{a(em(type_str()))}{description}{p(default_str(), sep())}\n"
|
||||||
|
|
||||||
|
def example_str(example: Any) -> str:
|
||||||
|
return "```yaml\n" + f"{yaml.dump({prop: example}, sort_keys=False)}" + "```\n"
|
||||||
|
|
||||||
|
def default_example() -> str:
|
||||||
|
if is_simple_default():
|
||||||
|
return ""
|
||||||
|
default_cfg = example_str(values["default"])
|
||||||
|
return f"\nDefault configuration:\n{default_cfg}"
|
||||||
|
|
||||||
|
def examples() -> str:
|
||||||
|
if not (examples := values.get("examples")):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
examples_str = "\n".join(example_str(e) for e in examples)
|
||||||
|
|
||||||
|
if len(examples) >= 2:
|
||||||
|
return f"\nExample configurations:\n{examples_str}"
|
||||||
|
else:
|
||||||
|
return f"\nExample configuration:\n{examples_str}"
|
||||||
|
|
||||||
|
def post_description() -> str:
|
||||||
|
# Sometimes it's helpful to have a description after the list of fields,
|
||||||
|
# e.g. with a subsection that consists only of text.
|
||||||
|
# This helps with that.
|
||||||
|
if not (description := values.get("io.element.post_description")):
|
||||||
|
return ""
|
||||||
|
return f"\n{description}\n\n"
|
||||||
|
|
||||||
|
return (
|
||||||
|
"---\n"
|
||||||
|
+ header()
|
||||||
|
+ title()
|
||||||
|
+ description()
|
||||||
|
+ items()
|
||||||
|
+ properties()
|
||||||
|
+ default_example()
|
||||||
|
+ examples()
|
||||||
|
+ post_description()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
def usage(err_msg: str) -> int:
|
||||||
|
script_name = (sys.argv[:1] or ["__main__.py"])[0]
|
||||||
|
print(err_msg, file=sys.stderr)
|
||||||
|
print(f"Usage: {script_name} <JSON Schema file>", file=sys.stderr)
|
||||||
|
print(f"\n{__doc__}", file=sys.stderr)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
def read_json_file_arg() -> Any:
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
exit(usage("Too many arguments."))
|
||||||
|
if not (filepath := (sys.argv[1:] or [""])[0]):
|
||||||
|
exit(usage("No schema file provided."))
|
||||||
|
with open(filepath) as f:
|
||||||
|
return yaml.safe_load(f)
|
||||||
|
|
||||||
|
schema = read_json_file_arg()
|
||||||
|
schema = resolve_local_refs(schema)
|
||||||
|
|
||||||
|
sections = (section(k, v) for k, v in schema["properties"].items())
|
||||||
|
print(HEADER + "".join(sections), end="")
|
||||||
|
|
||||||
|
if has_error:
|
||||||
|
print("There were errors.", file=sys.stderr)
|
||||||
|
exit(2)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
@ -139,3 +139,6 @@ cargo-fmt
|
|||||||
|
|
||||||
# Ensure type hints are correct.
|
# Ensure type hints are correct.
|
||||||
mypy
|
mypy
|
||||||
|
|
||||||
|
# Generate configuration documentation from the JSON Schema
|
||||||
|
./scripts-dev/gen_config_documentation.py schema/synapse-config.schema.yaml > docs/usage/configuration/config_documentation.md
|
||||||
|
@ -254,6 +254,12 @@ def _prepare() -> None:
|
|||||||
# Update the version specified in pyproject.toml.
|
# Update the version specified in pyproject.toml.
|
||||||
subprocess.check_output(["poetry", "version", new_version])
|
subprocess.check_output(["poetry", "version", new_version])
|
||||||
|
|
||||||
|
# Update config schema $id.
|
||||||
|
schema_file = "schema/synapse-config.schema.yaml"
|
||||||
|
major_minor_version = ".".join(new_version.split(".")[:2])
|
||||||
|
url = f"https://element-hq.github.io/synapse/schema/synapse/v{major_minor_version}/synapse-config.schema.json"
|
||||||
|
subprocess.check_output(["sed", "-i", f"0,/^\\$id: .*/s||$id: {url}|", schema_file])
|
||||||
|
|
||||||
# Generate changelogs.
|
# Generate changelogs.
|
||||||
generate_and_write_changelog(synapse_repo, current_version, new_version)
|
generate_and_write_changelog(synapse_repo, current_version, new_version)
|
||||||
|
|
||||||
@ -592,7 +598,7 @@ def _wait_for_actions(gh_token: Optional[str]) -> None:
|
|||||||
if all(
|
if all(
|
||||||
workflow["status"] != "in_progress" for workflow in resp["workflow_runs"]
|
workflow["status"] != "in_progress" for workflow in resp["workflow_runs"]
|
||||||
):
|
):
|
||||||
success = (
|
success = all(
|
||||||
workflow["status"] == "completed" for workflow in resp["workflow_runs"]
|
workflow["status"] == "completed" for workflow in resp["workflow_runs"]
|
||||||
)
|
)
|
||||||
if success:
|
if success:
|
||||||
|
@ -128,6 +128,7 @@ BOOLEAN_COLUMNS = {
|
|||||||
"pushers": ["enabled"],
|
"pushers": ["enabled"],
|
||||||
"redactions": ["have_censored"],
|
"redactions": ["have_censored"],
|
||||||
"remote_media_cache": ["authenticated"],
|
"remote_media_cache": ["authenticated"],
|
||||||
|
"room_memberships": ["participant"],
|
||||||
"room_stats_state": ["is_federatable"],
|
"room_stats_state": ["is_federatable"],
|
||||||
"rooms": ["is_public", "has_auth_chain_index"],
|
"rooms": ["is_public", "has_auth_chain_index"],
|
||||||
"sliding_sync_joined_rooms": ["is_encrypted"],
|
"sliding_sync_joined_rooms": ["is_encrypted"],
|
||||||
@ -194,7 +195,7 @@ IGNORED_TABLES = {
|
|||||||
# Porting the auto generated sequence in this table is non-trivial.
|
# Porting the auto generated sequence in this table is non-trivial.
|
||||||
# None of the entries in this list are mandatory for Synapse to keep working.
|
# None of the entries in this list are mandatory for Synapse to keep working.
|
||||||
# If state group disk space is an issue after the port, the
|
# If state group disk space is an issue after the port, the
|
||||||
# `delete_unreferenced_state_groups_bg_update` background task can be run again.
|
# `mark_unreferenced_state_groups_for_deletion_bg_update` background task can be run again.
|
||||||
"state_groups_pending_deletion",
|
"state_groups_pending_deletion",
|
||||||
# We don't port these tables, as they're a faff and we can regenerate
|
# We don't port these tables, as they're a faff and we can regenerate
|
||||||
# them anyway.
|
# them anyway.
|
||||||
@ -226,7 +227,7 @@ IGNORED_BACKGROUND_UPDATES = {
|
|||||||
# Reapplying this background update to the postgres database is unnecessary after
|
# Reapplying this background update to the postgres database is unnecessary after
|
||||||
# already having waited for the SQLite database to complete all running background
|
# already having waited for the SQLite database to complete all running background
|
||||||
# updates.
|
# updates.
|
||||||
"delete_unreferenced_state_groups_bg_update",
|
"mark_unreferenced_state_groups_for_deletion_bg_update",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1064,7 +1065,7 @@ class Porter:
|
|||||||
|
|
||||||
def get_sent_table_size(txn: LoggingTransaction) -> int:
|
def get_sent_table_size(txn: LoggingTransaction) -> int:
|
||||||
txn.execute(
|
txn.execute(
|
||||||
"SELECT count(*) FROM sent_transactions" " WHERE ts >= ?", (yesterday,)
|
"SELECT count(*) FROM sent_transactions WHERE ts >= ?", (yesterday,)
|
||||||
)
|
)
|
||||||
result = txn.fetchone()
|
result = txn.fetchone()
|
||||||
assert result is not None
|
assert result is not None
|
||||||
|
@ -292,9 +292,9 @@ def main() -> None:
|
|||||||
for key in worker_config:
|
for key in worker_config:
|
||||||
if key == "worker_app": # But we allow worker_app
|
if key == "worker_app": # But we allow worker_app
|
||||||
continue
|
continue
|
||||||
assert not key.startswith(
|
assert not key.startswith("worker_"), (
|
||||||
"worker_"
|
"Main process cannot use worker_* config"
|
||||||
), "Main process cannot use worker_* config"
|
)
|
||||||
else:
|
else:
|
||||||
worker_pidfile = worker_config["worker_pid_file"]
|
worker_pidfile = worker_config["worker_pid_file"]
|
||||||
worker_cache_factor = worker_config.get("synctl_cache_factor")
|
worker_cache_factor = worker_config.get("synctl_cache_factor")
|
||||||
|
@ -37,7 +37,9 @@ from synapse.appservice import ApplicationService
|
|||||||
from synapse.http import get_request_user_agent
|
from synapse.http import get_request_user_agent
|
||||||
from synapse.http.site import SynapseRequest
|
from synapse.http.site import SynapseRequest
|
||||||
from synapse.logging.opentracing import trace
|
from synapse.logging.opentracing import trace
|
||||||
|
from synapse.state import CREATE_KEY, POWER_KEY
|
||||||
from synapse.types import Requester, create_requester
|
from synapse.types import Requester, create_requester
|
||||||
|
from synapse.types.state import StateFilter
|
||||||
from synapse.util.cancellation import cancellable
|
from synapse.util.cancellation import cancellable
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -216,18 +218,20 @@ class BaseAuth:
|
|||||||
# by checking if they would (theoretically) be able to change the
|
# by checking if they would (theoretically) be able to change the
|
||||||
# m.room.canonical_alias events
|
# m.room.canonical_alias events
|
||||||
|
|
||||||
power_level_event = (
|
auth_events = await self._storage_controllers.state.get_current_state(
|
||||||
await self._storage_controllers.state.get_current_state_event(
|
room_id,
|
||||||
room_id, EventTypes.PowerLevels, ""
|
StateFilter.from_types(
|
||||||
)
|
[
|
||||||
|
POWER_KEY,
|
||||||
|
CREATE_KEY,
|
||||||
|
]
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
auth_events = {}
|
|
||||||
if power_level_event:
|
|
||||||
auth_events[(EventTypes.PowerLevels, "")] = power_level_event
|
|
||||||
|
|
||||||
send_level = event_auth.get_send_level(
|
send_level = event_auth.get_send_level(
|
||||||
EventTypes.CanonicalAlias, "", power_level_event
|
EventTypes.CanonicalAlias,
|
||||||
|
"",
|
||||||
|
auth_events.get(POWER_KEY),
|
||||||
)
|
)
|
||||||
user_level = event_auth.get_user_power_level(
|
user_level = event_auth.get_user_power_level(
|
||||||
requester.user.to_string(), auth_events
|
requester.user.to_string(), auth_events
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
import logging
|
import logging
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional
|
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
@ -29,24 +30,28 @@ from authlib.oauth2.rfc7662 import IntrospectionToken
|
|||||||
from authlib.oidc.discovery import OpenIDProviderMetadata, get_well_known_url
|
from authlib.oidc.discovery import OpenIDProviderMetadata, get_well_known_url
|
||||||
from prometheus_client import Histogram
|
from prometheus_client import Histogram
|
||||||
|
|
||||||
from twisted.web.client import readBody
|
|
||||||
from twisted.web.http_headers import Headers
|
|
||||||
|
|
||||||
from synapse.api.auth.base import BaseAuth
|
from synapse.api.auth.base import BaseAuth
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
AuthError,
|
AuthError,
|
||||||
HttpResponseException,
|
HttpResponseException,
|
||||||
InvalidClientTokenError,
|
InvalidClientTokenError,
|
||||||
OAuthInsufficientScopeError,
|
OAuthInsufficientScopeError,
|
||||||
StoreError,
|
|
||||||
SynapseError,
|
SynapseError,
|
||||||
UnrecognizedRequestError,
|
UnrecognizedRequestError,
|
||||||
)
|
)
|
||||||
from synapse.http.site import SynapseRequest
|
from synapse.http.site import SynapseRequest
|
||||||
from synapse.logging.context import make_deferred_yieldable
|
from synapse.logging.context import PreserveLoggingContext
|
||||||
|
from synapse.logging.opentracing import (
|
||||||
|
active_span,
|
||||||
|
force_tracing,
|
||||||
|
inject_request_headers,
|
||||||
|
start_active_span,
|
||||||
|
)
|
||||||
|
from synapse.synapse_rust.http_client import HttpClient
|
||||||
from synapse.types import Requester, UserID, create_requester
|
from synapse.types import Requester, UserID, create_requester
|
||||||
from synapse.util import json_decoder
|
from synapse.util import json_decoder
|
||||||
from synapse.util.caches.cached_call import RetryOnExceptionCachedCall
|
from synapse.util.caches.cached_call import RetryOnExceptionCachedCall
|
||||||
|
from synapse.util.caches.response_cache import ResponseCache, ResponseCacheContext
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.rest.admin.experimental_features import ExperimentalFeature
|
from synapse.rest.admin.experimental_features import ExperimentalFeature
|
||||||
@ -76,6 +81,61 @@ def scope_to_list(scope: str) -> List[str]:
|
|||||||
return scope.strip().split(" ")
|
return scope.strip().split(" ")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class IntrospectionResult:
|
||||||
|
_inner: IntrospectionToken
|
||||||
|
|
||||||
|
# when we retrieved this token,
|
||||||
|
# in milliseconds since the Unix epoch
|
||||||
|
retrieved_at_ms: int
|
||||||
|
|
||||||
|
def is_active(self, now_ms: int) -> bool:
|
||||||
|
if not self._inner.get("active"):
|
||||||
|
return False
|
||||||
|
|
||||||
|
expires_in = self._inner.get("expires_in")
|
||||||
|
if expires_in is None:
|
||||||
|
return True
|
||||||
|
if not isinstance(expires_in, int):
|
||||||
|
raise InvalidClientTokenError("token `expires_in` is not an int")
|
||||||
|
|
||||||
|
absolute_expiry_ms = expires_in * 1000 + self.retrieved_at_ms
|
||||||
|
return now_ms < absolute_expiry_ms
|
||||||
|
|
||||||
|
def get_scope_list(self) -> List[str]:
|
||||||
|
value = self._inner.get("scope")
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return []
|
||||||
|
return scope_to_list(value)
|
||||||
|
|
||||||
|
def get_sub(self) -> Optional[str]:
|
||||||
|
value = self._inner.get("sub")
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return None
|
||||||
|
return value
|
||||||
|
|
||||||
|
def get_username(self) -> Optional[str]:
|
||||||
|
value = self._inner.get("username")
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return None
|
||||||
|
return value
|
||||||
|
|
||||||
|
def get_name(self) -> Optional[str]:
|
||||||
|
value = self._inner.get("name")
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return None
|
||||||
|
return value
|
||||||
|
|
||||||
|
def get_device_id(self) -> Optional[str]:
|
||||||
|
value = self._inner.get("device_id")
|
||||||
|
if value is not None and not isinstance(value, str):
|
||||||
|
raise AuthError(
|
||||||
|
500,
|
||||||
|
"Invalid device ID in introspection result",
|
||||||
|
)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class PrivateKeyJWTWithKid(PrivateKeyJWT): # type: ignore[misc]
|
class PrivateKeyJWTWithKid(PrivateKeyJWT): # type: ignore[misc]
|
||||||
"""An implementation of the private_key_jwt client auth method that includes a kid header.
|
"""An implementation of the private_key_jwt client auth method that includes a kid header.
|
||||||
|
|
||||||
@ -120,6 +180,38 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
self._http_client = hs.get_proxied_http_client()
|
self._http_client = hs.get_proxied_http_client()
|
||||||
self._hostname = hs.hostname
|
self._hostname = hs.hostname
|
||||||
self._admin_token: Callable[[], Optional[str]] = self._config.admin_token
|
self._admin_token: Callable[[], Optional[str]] = self._config.admin_token
|
||||||
|
self._force_tracing_for_users = hs.config.tracing.force_tracing_for_users
|
||||||
|
|
||||||
|
self._rust_http_client = HttpClient(
|
||||||
|
user_agent=self._http_client.user_agent.decode("utf8")
|
||||||
|
)
|
||||||
|
|
||||||
|
# # Token Introspection Cache
|
||||||
|
# This remembers what users/devices are represented by which access tokens,
|
||||||
|
# in order to reduce overall system load:
|
||||||
|
# - on Synapse (as requests are relatively expensive)
|
||||||
|
# - on the network
|
||||||
|
# - on MAS
|
||||||
|
#
|
||||||
|
# Since there is no invalidation mechanism currently,
|
||||||
|
# the entries expire after 2 minutes.
|
||||||
|
# This does mean tokens can be treated as valid by Synapse
|
||||||
|
# for longer than reality.
|
||||||
|
#
|
||||||
|
# Ideally, tokens should logically be invalidated in the following circumstances:
|
||||||
|
# - If a session logout happens.
|
||||||
|
# In this case, MAS will delete the device within Synapse
|
||||||
|
# anyway and this is good enough as an invalidation.
|
||||||
|
# - If the client refreshes their token in MAS.
|
||||||
|
# In this case, the device still exists and it's not the end of the world for
|
||||||
|
# the old access token to continue working for a short time.
|
||||||
|
self._introspection_cache: ResponseCache[str] = ResponseCache(
|
||||||
|
self._clock,
|
||||||
|
"token_introspection",
|
||||||
|
timeout_ms=120_000,
|
||||||
|
# don't log because the keys are access tokens
|
||||||
|
enable_logging=False,
|
||||||
|
)
|
||||||
|
|
||||||
self._issuer_metadata = RetryOnExceptionCachedCall[OpenIDProviderMetadata](
|
self._issuer_metadata = RetryOnExceptionCachedCall[OpenIDProviderMetadata](
|
||||||
self._load_metadata
|
self._load_metadata
|
||||||
@ -193,7 +285,9 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
metadata = await self._issuer_metadata.get()
|
metadata = await self._issuer_metadata.get()
|
||||||
return metadata.get("introspection_endpoint")
|
return metadata.get("introspection_endpoint")
|
||||||
|
|
||||||
async def _introspect_token(self, token: str) -> IntrospectionToken:
|
async def _introspect_token(
|
||||||
|
self, token: str, cache_context: ResponseCacheContext[str]
|
||||||
|
) -> IntrospectionResult:
|
||||||
"""
|
"""
|
||||||
Send a token to the introspection endpoint and returns the introspection response
|
Send a token to the introspection endpoint and returns the introspection response
|
||||||
|
|
||||||
@ -209,10 +303,11 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
Returns:
|
Returns:
|
||||||
The introspection response
|
The introspection response
|
||||||
"""
|
"""
|
||||||
|
# By default, we shouldn't cache the result unless we know it's valid
|
||||||
|
cache_context.should_cache = False
|
||||||
introspection_endpoint = await self._introspection_endpoint()
|
introspection_endpoint = await self._introspection_endpoint()
|
||||||
raw_headers: Dict[str, str] = {
|
raw_headers: Dict[str, str] = {
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
"User-Agent": str(self._http_client.user_agent, "utf-8"),
|
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
# Tell MAS that we support reading the device ID as an explicit
|
# Tell MAS that we support reading the device ID as an explicit
|
||||||
# value, not encoded in the scope. This is supported by MAS 0.15+
|
# value, not encoded in the scope. This is supported by MAS 0.15+
|
||||||
@ -226,38 +321,34 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
uri, raw_headers, body = self._client_auth.prepare(
|
uri, raw_headers, body = self._client_auth.prepare(
|
||||||
method="POST", uri=introspection_endpoint, headers=raw_headers, body=body
|
method="POST", uri=introspection_endpoint, headers=raw_headers, body=body
|
||||||
)
|
)
|
||||||
headers = Headers({k: [v] for (k, v) in raw_headers.items()})
|
|
||||||
|
|
||||||
# Do the actual request
|
# Do the actual request
|
||||||
# We're not using the SimpleHttpClient util methods as we don't want to
|
|
||||||
# check the HTTP status code, and we do the body encoding ourselves.
|
|
||||||
|
|
||||||
|
logger.debug("Fetching token from MAS")
|
||||||
start_time = self._clock.time()
|
start_time = self._clock.time()
|
||||||
try:
|
try:
|
||||||
response = await self._http_client.request(
|
with start_active_span("mas-introspect-token"):
|
||||||
method="POST",
|
inject_request_headers(raw_headers)
|
||||||
uri=uri,
|
with PreserveLoggingContext():
|
||||||
data=body.encode("utf-8"),
|
resp_body = await self._rust_http_client.post(
|
||||||
headers=headers,
|
url=uri,
|
||||||
)
|
response_limit=1 * 1024 * 1024,
|
||||||
|
headers=raw_headers,
|
||||||
resp_body = await make_deferred_yieldable(readBody(response))
|
request_body=body,
|
||||||
|
)
|
||||||
|
except HttpResponseException as e:
|
||||||
|
end_time = self._clock.time()
|
||||||
|
introspection_response_timer.labels(e.code).observe(end_time - start_time)
|
||||||
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
end_time = self._clock.time()
|
end_time = self._clock.time()
|
||||||
introspection_response_timer.labels("ERR").observe(end_time - start_time)
|
introspection_response_timer.labels("ERR").observe(end_time - start_time)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
end_time = self._clock.time()
|
logger.debug("Fetched token from MAS")
|
||||||
introspection_response_timer.labels(response.code).observe(
|
|
||||||
end_time - start_time
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.code < 200 or response.code >= 300:
|
end_time = self._clock.time()
|
||||||
raise HttpResponseException(
|
introspection_response_timer.labels(200).observe(end_time - start_time)
|
||||||
response.code,
|
|
||||||
response.phrase.decode("ascii", errors="replace"),
|
|
||||||
resp_body,
|
|
||||||
)
|
|
||||||
|
|
||||||
resp = json_decoder.decode(resp_body.decode("utf-8"))
|
resp = json_decoder.decode(resp_body.decode("utf-8"))
|
||||||
|
|
||||||
@ -266,7 +357,11 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
"The introspection endpoint returned an invalid JSON response."
|
"The introspection endpoint returned an invalid JSON response."
|
||||||
)
|
)
|
||||||
|
|
||||||
return IntrospectionToken(**resp)
|
# We had a valid response, so we can cache it
|
||||||
|
cache_context.should_cache = True
|
||||||
|
return IntrospectionResult(
|
||||||
|
IntrospectionToken(**resp), retrieved_at_ms=self._clock.time_msec()
|
||||||
|
)
|
||||||
|
|
||||||
async def is_server_admin(self, requester: Requester) -> bool:
|
async def is_server_admin(self, requester: Requester) -> bool:
|
||||||
return "urn:synapse:admin:*" in requester.scope
|
return "urn:synapse:admin:*" in requester.scope
|
||||||
@ -277,6 +372,55 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
allow_guest: bool = False,
|
allow_guest: bool = False,
|
||||||
allow_expired: bool = False,
|
allow_expired: bool = False,
|
||||||
allow_locked: bool = False,
|
allow_locked: bool = False,
|
||||||
|
) -> Requester:
|
||||||
|
"""Get a registered user's ID.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: An HTTP request with an access_token query parameter.
|
||||||
|
allow_guest: If False, will raise an AuthError if the user making the
|
||||||
|
request is a guest.
|
||||||
|
allow_expired: If True, allow the request through even if the account
|
||||||
|
is expired, or session token lifetime has ended. Note that
|
||||||
|
/login will deliver access tokens regardless of expiration.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Resolves to the requester
|
||||||
|
Raises:
|
||||||
|
InvalidClientCredentialsError if no user by that token exists or the token
|
||||||
|
is invalid.
|
||||||
|
AuthError if access is denied for the user in the access token
|
||||||
|
"""
|
||||||
|
parent_span = active_span()
|
||||||
|
with start_active_span("get_user_by_req"):
|
||||||
|
requester = await self._wrapped_get_user_by_req(
|
||||||
|
request, allow_guest, allow_expired, allow_locked
|
||||||
|
)
|
||||||
|
|
||||||
|
if parent_span:
|
||||||
|
if requester.authenticated_entity in self._force_tracing_for_users:
|
||||||
|
# request tracing is enabled for this user, so we need to force it
|
||||||
|
# tracing on for the parent span (which will be the servlet span).
|
||||||
|
#
|
||||||
|
# It's too late for the get_user_by_req span to inherit the setting,
|
||||||
|
# so we also force it on for that.
|
||||||
|
force_tracing()
|
||||||
|
force_tracing(parent_span)
|
||||||
|
parent_span.set_tag(
|
||||||
|
"authenticated_entity", requester.authenticated_entity
|
||||||
|
)
|
||||||
|
parent_span.set_tag("user_id", requester.user.to_string())
|
||||||
|
if requester.device_id is not None:
|
||||||
|
parent_span.set_tag("device_id", requester.device_id)
|
||||||
|
if requester.app_service is not None:
|
||||||
|
parent_span.set_tag("appservice_id", requester.app_service.id)
|
||||||
|
return requester
|
||||||
|
|
||||||
|
async def _wrapped_get_user_by_req(
|
||||||
|
self,
|
||||||
|
request: SynapseRequest,
|
||||||
|
allow_guest: bool = False,
|
||||||
|
allow_expired: bool = False,
|
||||||
|
allow_locked: bool = False,
|
||||||
) -> Requester:
|
) -> Requester:
|
||||||
access_token = self.get_access_token_from_request(request)
|
access_token = self.get_access_token_from_request(request)
|
||||||
|
|
||||||
@ -333,7 +477,7 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
# XXX: This is a temporary solution so that the admin API can be called by
|
# XXX: This is a temporary solution so that the admin API can be called by
|
||||||
# the OIDC provider. This will be removed once we have OIDC client
|
# the OIDC provider. This will be removed once we have OIDC client
|
||||||
# credentials grant support in matrix-authentication-service.
|
# credentials grant support in matrix-authentication-service.
|
||||||
logging.info("Admin toked used")
|
logger.info("Admin toked used")
|
||||||
# XXX: that user doesn't exist and won't be provisioned.
|
# XXX: that user doesn't exist and won't be provisioned.
|
||||||
# This is mostly fine for admin calls, but we should also think about doing
|
# This is mostly fine for admin calls, but we should also think about doing
|
||||||
# requesters without a user_id.
|
# requesters without a user_id.
|
||||||
@ -344,7 +488,9 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
introspection_result = await self._introspect_token(token)
|
introspection_result = await self._introspection_cache.wrap(
|
||||||
|
token, self._introspect_token, token, cache_context=True
|
||||||
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Failed to introspect token")
|
logger.exception("Failed to introspect token")
|
||||||
raise SynapseError(503, "Unable to introspect the access token")
|
raise SynapseError(503, "Unable to introspect the access token")
|
||||||
@ -353,11 +499,11 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
|
|
||||||
# TODO: introspection verification should be more extensive, especially:
|
# TODO: introspection verification should be more extensive, especially:
|
||||||
# - verify the audience
|
# - verify the audience
|
||||||
if not introspection_result.get("active"):
|
if not introspection_result.is_active(self._clock.time_msec()):
|
||||||
raise InvalidClientTokenError("Token is not active")
|
raise InvalidClientTokenError("Token is not active")
|
||||||
|
|
||||||
# Let's look at the scope
|
# Let's look at the scope
|
||||||
scope: List[str] = scope_to_list(introspection_result.get("scope", ""))
|
scope: List[str] = introspection_result.get_scope_list()
|
||||||
|
|
||||||
# Determine type of user based on presence of particular scopes
|
# Determine type of user based on presence of particular scopes
|
||||||
has_user_scope = SCOPE_MATRIX_API in scope
|
has_user_scope = SCOPE_MATRIX_API in scope
|
||||||
@ -367,7 +513,7 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
raise InvalidClientTokenError("No scope in token granting user rights")
|
raise InvalidClientTokenError("No scope in token granting user rights")
|
||||||
|
|
||||||
# Match via the sub claim
|
# Match via the sub claim
|
||||||
sub: Optional[str] = introspection_result.get("sub")
|
sub = introspection_result.get_sub()
|
||||||
if sub is None:
|
if sub is None:
|
||||||
raise InvalidClientTokenError(
|
raise InvalidClientTokenError(
|
||||||
"Invalid sub claim in the introspection result"
|
"Invalid sub claim in the introspection result"
|
||||||
@ -380,29 +526,20 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
# If we could not find a user via the external_id, it either does not exist,
|
# If we could not find a user via the external_id, it either does not exist,
|
||||||
# or the external_id was never recorded
|
# or the external_id was never recorded
|
||||||
|
|
||||||
# TODO: claim mapping should be configurable
|
username = introspection_result.get_username()
|
||||||
username: Optional[str] = introspection_result.get("username")
|
if username is None:
|
||||||
if username is None or not isinstance(username, str):
|
|
||||||
raise AuthError(
|
raise AuthError(
|
||||||
500,
|
500,
|
||||||
"Invalid username claim in the introspection result",
|
"Invalid username claim in the introspection result",
|
||||||
)
|
)
|
||||||
user_id = UserID(username, self._hostname)
|
user_id = UserID(username, self._hostname)
|
||||||
|
|
||||||
# First try to find a user from the username claim
|
# Try to find a user from the username claim
|
||||||
user_info = await self.store.get_user_by_id(user_id=user_id.to_string())
|
user_info = await self.store.get_user_by_id(user_id=user_id.to_string())
|
||||||
if user_info is None:
|
if user_info is None:
|
||||||
# If the user does not exist, we should create it on the fly
|
raise AuthError(
|
||||||
# TODO: we could use SCIM to provision users ahead of time and listen
|
500,
|
||||||
# for SCIM SET events if those ever become standard:
|
"User not found",
|
||||||
# https://datatracker.ietf.org/doc/html/draft-hunt-scim-notify-00
|
|
||||||
|
|
||||||
# TODO: claim mapping should be configurable
|
|
||||||
# If present, use the name claim as the displayname
|
|
||||||
name: Optional[str] = introspection_result.get("name")
|
|
||||||
|
|
||||||
await self.store.register_user(
|
|
||||||
user_id=user_id.to_string(), create_profile_with_displayname=name
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# And record the sub as external_id
|
# And record the sub as external_id
|
||||||
@ -414,15 +551,8 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
|
|
||||||
# MAS 0.15+ will give us the device ID as an explicit value for compatibility sessions
|
# MAS 0.15+ will give us the device ID as an explicit value for compatibility sessions
|
||||||
# If present, we get it from here, if not we get it in thee scope
|
# If present, we get it from here, if not we get it in thee scope
|
||||||
device_id = introspection_result.get("device_id")
|
device_id = introspection_result.get_device_id()
|
||||||
if device_id is not None:
|
if device_id is None:
|
||||||
# We got the device ID explicitly, just sanity check that it's a string
|
|
||||||
if not isinstance(device_id, str):
|
|
||||||
raise AuthError(
|
|
||||||
500,
|
|
||||||
"Invalid device ID in introspection result",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Find device_ids in scope
|
# Find device_ids in scope
|
||||||
# We only allow a single device_id in the scope, so we find them all in the
|
# We only allow a single device_id in the scope, so we find them all in the
|
||||||
# scope list, and raise if there are more than one. The OIDC server should be
|
# scope list, and raise if there are more than one. The OIDC server should be
|
||||||
@ -449,17 +579,10 @@ class MSC3861DelegatedAuth(BaseAuth):
|
|||||||
"Invalid device ID in introspection result",
|
"Invalid device ID in introspection result",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create the device on the fly if it does not exist
|
# Make sure the device exists
|
||||||
try:
|
await self.store.get_device(
|
||||||
await self.store.get_device(
|
user_id=user_id.to_string(), device_id=device_id
|
||||||
user_id=user_id.to_string(), device_id=device_id
|
)
|
||||||
)
|
|
||||||
except StoreError:
|
|
||||||
await self.store.store_device(
|
|
||||||
user_id=user_id.to_string(),
|
|
||||||
device_id=device_id,
|
|
||||||
initial_device_display_name="OIDC-native client",
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO: there is a few things missing in the requester here, which still need
|
# TODO: there is a few things missing in the requester here, which still need
|
||||||
# to be figured out, like:
|
# to be figured out, like:
|
||||||
|
@ -29,8 +29,13 @@ from typing import Final
|
|||||||
# the max size of a (canonical-json-encoded) event
|
# the max size of a (canonical-json-encoded) event
|
||||||
MAX_PDU_SIZE = 65536
|
MAX_PDU_SIZE = 65536
|
||||||
|
|
||||||
# the "depth" field on events is limited to 2**63 - 1
|
# Max/min size of ints in canonical JSON
|
||||||
MAX_DEPTH = 2**63 - 1
|
CANONICALJSON_MAX_INT = (2**53) - 1
|
||||||
|
CANONICALJSON_MIN_INT = -CANONICALJSON_MAX_INT
|
||||||
|
|
||||||
|
# the "depth" field on events is limited to the same as what
|
||||||
|
# canonicaljson accepts
|
||||||
|
MAX_DEPTH = CANONICALJSON_MAX_INT
|
||||||
|
|
||||||
# the maximum length for a room alias is 255 characters
|
# the maximum length for a room alias is 255 characters
|
||||||
MAX_ALIAS_LENGTH = 255
|
MAX_ALIAS_LENGTH = 255
|
||||||
@ -180,12 +185,18 @@ ServerNoticeLimitReached: Final = "m.server_notice.usage_limit_reached"
|
|||||||
|
|
||||||
class UserTypes:
|
class UserTypes:
|
||||||
"""Allows for user type specific behaviour. With the benefit of hindsight
|
"""Allows for user type specific behaviour. With the benefit of hindsight
|
||||||
'admin' and 'guest' users should also be UserTypes. Normal users are type None
|
'admin' and 'guest' users should also be UserTypes. Extra user types can be
|
||||||
|
added in the configuration. Normal users are type None or one of the extra
|
||||||
|
user types (if configured).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SUPPORT: Final = "support"
|
SUPPORT: Final = "support"
|
||||||
BOT: Final = "bot"
|
BOT: Final = "bot"
|
||||||
ALL_USER_TYPES: Final = (SUPPORT, BOT)
|
ALL_BUILTIN_USER_TYPES: Final = (SUPPORT, BOT)
|
||||||
|
"""
|
||||||
|
The user types that are built-in to Synapse. Extra user types can be
|
||||||
|
added in the configuration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class RelationTypes:
|
class RelationTypes:
|
||||||
@ -275,6 +286,10 @@ class AccountDataTypes:
|
|||||||
IGNORED_USER_LIST: Final = "m.ignored_user_list"
|
IGNORED_USER_LIST: Final = "m.ignored_user_list"
|
||||||
TAG: Final = "m.tag"
|
TAG: Final = "m.tag"
|
||||||
PUSH_RULES: Final = "m.push_rules"
|
PUSH_RULES: Final = "m.push_rules"
|
||||||
|
# MSC4155: Invite filtering
|
||||||
|
MSC4155_INVITE_PERMISSION_CONFIG: Final = (
|
||||||
|
"org.matrix.msc4155.invite_permission_config"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class HistoryVisibility:
|
class HistoryVisibility:
|
||||||
|
@ -70,6 +70,7 @@ class Codes(str, Enum):
|
|||||||
THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND"
|
THREEPID_NOT_FOUND = "M_THREEPID_NOT_FOUND"
|
||||||
THREEPID_DENIED = "M_THREEPID_DENIED"
|
THREEPID_DENIED = "M_THREEPID_DENIED"
|
||||||
INVALID_USERNAME = "M_INVALID_USERNAME"
|
INVALID_USERNAME = "M_INVALID_USERNAME"
|
||||||
|
THREEPID_MEDIUM_NOT_SUPPORTED = "M_THREEPID_MEDIUM_NOT_SUPPORTED"
|
||||||
SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED"
|
SERVER_NOT_TRUSTED = "M_SERVER_NOT_TRUSTED"
|
||||||
CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
|
CONSENT_NOT_GIVEN = "M_CONSENT_NOT_GIVEN"
|
||||||
CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM"
|
CANNOT_LEAVE_SERVER_NOTICE_ROOM = "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM"
|
||||||
@ -136,6 +137,9 @@ class Codes(str, Enum):
|
|||||||
PROFILE_TOO_LARGE = "M_PROFILE_TOO_LARGE"
|
PROFILE_TOO_LARGE = "M_PROFILE_TOO_LARGE"
|
||||||
KEY_TOO_LARGE = "M_KEY_TOO_LARGE"
|
KEY_TOO_LARGE = "M_KEY_TOO_LARGE"
|
||||||
|
|
||||||
|
# Part of MSC4155
|
||||||
|
INVITE_BLOCKED = "ORG.MATRIX.MSC4155.M_INVITE_BLOCKED"
|
||||||
|
|
||||||
|
|
||||||
class CodeMessageException(RuntimeError):
|
class CodeMessageException(RuntimeError):
|
||||||
"""An exception with integer code, a message string attributes and optional headers.
|
"""An exception with integer code, a message string attributes and optional headers.
|
||||||
@ -523,7 +527,11 @@ class InvalidCaptchaError(SynapseError):
|
|||||||
|
|
||||||
|
|
||||||
class LimitExceededError(SynapseError):
|
class LimitExceededError(SynapseError):
|
||||||
"""A client has sent too many requests and is being throttled."""
|
"""A client has sent too many requests and is being throttled.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pause: Optional time in seconds to pause before responding to the client.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -531,6 +539,7 @@ class LimitExceededError(SynapseError):
|
|||||||
code: int = 429,
|
code: int = 429,
|
||||||
retry_after_ms: Optional[int] = None,
|
retry_after_ms: Optional[int] = None,
|
||||||
errcode: str = Codes.LIMIT_EXCEEDED,
|
errcode: str = Codes.LIMIT_EXCEEDED,
|
||||||
|
pause: Optional[float] = None,
|
||||||
):
|
):
|
||||||
# Use HTTP header Retry-After to enable library-assisted retry handling.
|
# Use HTTP header Retry-After to enable library-assisted retry handling.
|
||||||
headers = (
|
headers = (
|
||||||
@ -541,6 +550,7 @@ class LimitExceededError(SynapseError):
|
|||||||
super().__init__(code, "Too Many Requests", errcode, headers=headers)
|
super().__init__(code, "Too Many Requests", errcode, headers=headers)
|
||||||
self.retry_after_ms = retry_after_ms
|
self.retry_after_ms = retry_after_ms
|
||||||
self.limiter_name = limiter_name
|
self.limiter_name = limiter_name
|
||||||
|
self.pause = pause
|
||||||
|
|
||||||
def error_dict(self, config: Optional["HomeServerConfig"]) -> "JsonDict":
|
def error_dict(self, config: Optional["HomeServerConfig"]) -> "JsonDict":
|
||||||
return cs_error(self.msg, self.errcode, retry_after_ms=self.retry_after_ms)
|
return cs_error(self.msg, self.errcode, retry_after_ms=self.retry_after_ms)
|
||||||
|
@ -20,8 +20,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from collections import OrderedDict
|
from typing import TYPE_CHECKING, Dict, Hashable, Optional, Tuple
|
||||||
from typing import Hashable, Optional, Tuple
|
|
||||||
|
|
||||||
from synapse.api.errors import LimitExceededError
|
from synapse.api.errors import LimitExceededError
|
||||||
from synapse.config.ratelimiting import RatelimitSettings
|
from synapse.config.ratelimiting import RatelimitSettings
|
||||||
@ -29,6 +28,12 @@ from synapse.storage.databases.main import DataStore
|
|||||||
from synapse.types import Requester
|
from synapse.types import Requester
|
||||||
from synapse.util import Clock
|
from synapse.util import Clock
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
# To avoid circular imports:
|
||||||
|
from synapse.module_api.callbacks.ratelimit_callbacks import (
|
||||||
|
RatelimitModuleApiCallbacks,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Ratelimiter:
|
class Ratelimiter:
|
||||||
"""
|
"""
|
||||||
@ -73,19 +78,23 @@ class Ratelimiter:
|
|||||||
store: DataStore,
|
store: DataStore,
|
||||||
clock: Clock,
|
clock: Clock,
|
||||||
cfg: RatelimitSettings,
|
cfg: RatelimitSettings,
|
||||||
|
ratelimit_callbacks: Optional["RatelimitModuleApiCallbacks"] = None,
|
||||||
):
|
):
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
self.rate_hz = cfg.per_second
|
self.rate_hz = cfg.per_second
|
||||||
self.burst_count = cfg.burst_count
|
self.burst_count = cfg.burst_count
|
||||||
self.store = store
|
self.store = store
|
||||||
self._limiter_name = cfg.key
|
self._limiter_name = cfg.key
|
||||||
|
self._ratelimit_callbacks = ratelimit_callbacks
|
||||||
|
|
||||||
# An ordered dictionary representing the token buckets tracked by this rate
|
# A dictionary representing the token buckets tracked by this rate
|
||||||
# limiter. Each entry maps a key of arbitrary type to a tuple representing:
|
# limiter. Each entry maps a key of arbitrary type to a tuple representing:
|
||||||
# * The number of tokens currently in the bucket,
|
# * The number of tokens currently in the bucket,
|
||||||
# * The time point when the bucket was last completely empty, and
|
# * The time point when the bucket was last completely empty, and
|
||||||
# * The rate_hz (leak rate) of this particular bucket.
|
# * The rate_hz (leak rate) of this particular bucket.
|
||||||
self.actions: OrderedDict[Hashable, Tuple[float, float, float]] = OrderedDict()
|
self.actions: Dict[Hashable, Tuple[float, float, float]] = {}
|
||||||
|
|
||||||
|
self.clock.looping_call(self._prune_message_counts, 60 * 1000)
|
||||||
|
|
||||||
def _get_key(
|
def _get_key(
|
||||||
self, requester: Optional[Requester], key: Optional[Hashable]
|
self, requester: Optional[Requester], key: Optional[Hashable]
|
||||||
@ -164,14 +173,25 @@ class Ratelimiter:
|
|||||||
if override and not override.messages_per_second:
|
if override and not override.messages_per_second:
|
||||||
return True, -1.0
|
return True, -1.0
|
||||||
|
|
||||||
|
if requester and self._ratelimit_callbacks:
|
||||||
|
# Check if the user has a custom rate limit for this specific limiter
|
||||||
|
# as returned by the module API.
|
||||||
|
module_override = (
|
||||||
|
await self._ratelimit_callbacks.get_ratelimit_override_for_user(
|
||||||
|
requester.user.to_string(),
|
||||||
|
self._limiter_name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if module_override:
|
||||||
|
rate_hz = module_override.per_second
|
||||||
|
burst_count = module_override.burst_count
|
||||||
|
|
||||||
# Override default values if set
|
# Override default values if set
|
||||||
time_now_s = _time_now_s if _time_now_s is not None else self.clock.time()
|
time_now_s = _time_now_s if _time_now_s is not None else self.clock.time()
|
||||||
rate_hz = rate_hz if rate_hz is not None else self.rate_hz
|
rate_hz = rate_hz if rate_hz is not None else self.rate_hz
|
||||||
burst_count = burst_count if burst_count is not None else self.burst_count
|
burst_count = burst_count if burst_count is not None else self.burst_count
|
||||||
|
|
||||||
# Remove any expired entries
|
|
||||||
self._prune_message_counts(time_now_s)
|
|
||||||
|
|
||||||
# Check if there is an existing count entry for this key
|
# Check if there is an existing count entry for this key
|
||||||
action_count, time_start, _ = self._get_action_counts(key, time_now_s)
|
action_count, time_start, _ = self._get_action_counts(key, time_now_s)
|
||||||
|
|
||||||
@ -246,13 +266,12 @@ class Ratelimiter:
|
|||||||
action_count, time_start, rate_hz = self._get_action_counts(key, time_now_s)
|
action_count, time_start, rate_hz = self._get_action_counts(key, time_now_s)
|
||||||
self.actions[key] = (action_count + n_actions, time_start, rate_hz)
|
self.actions[key] = (action_count + n_actions, time_start, rate_hz)
|
||||||
|
|
||||||
def _prune_message_counts(self, time_now_s: float) -> None:
|
def _prune_message_counts(self) -> None:
|
||||||
"""Remove message count entries that have not exceeded their defined
|
"""Remove message count entries that have not exceeded their defined
|
||||||
rate_hz limit
|
rate_hz limit
|
||||||
|
|
||||||
Args:
|
|
||||||
time_now_s: The current time
|
|
||||||
"""
|
"""
|
||||||
|
time_now_s = self.clock.time()
|
||||||
|
|
||||||
# We create a copy of the key list here as the dictionary is modified during
|
# We create a copy of the key list here as the dictionary is modified during
|
||||||
# the loop
|
# the loop
|
||||||
for key in list(self.actions.keys()):
|
for key in list(self.actions.keys()):
|
||||||
@ -319,12 +338,10 @@ class Ratelimiter:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not allowed:
|
if not allowed:
|
||||||
if pause:
|
|
||||||
await self.clock.sleep(pause)
|
|
||||||
|
|
||||||
raise LimitExceededError(
|
raise LimitExceededError(
|
||||||
limiter_name=self._limiter_name,
|
limiter_name=self._limiter_name,
|
||||||
retry_after_ms=int(1000 * (time_allowed - time_now_s)),
|
retry_after_ms=int(1000 * (time_allowed - time_now_s)),
|
||||||
|
pause=pause,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -445,8 +445,8 @@ def listen_http(
|
|||||||
# getHost() returns a UNIXAddress which contains an instance variable of 'name'
|
# getHost() returns a UNIXAddress which contains an instance variable of 'name'
|
||||||
# encoded as a byte string. Decode as utf-8 so pretty.
|
# encoded as a byte string. Decode as utf-8 so pretty.
|
||||||
logger.info(
|
logger.info(
|
||||||
"Synapse now listening on Unix Socket at: "
|
"Synapse now listening on Unix Socket at: %s",
|
||||||
f"{ports[0].getHost().name.decode('utf-8')}"
|
ports[0].getHost().name.decode("utf-8"),
|
||||||
)
|
)
|
||||||
|
|
||||||
return ports
|
return ports
|
||||||
|
@ -51,8 +51,7 @@ from synapse.http.server import JsonResource, OptionsResource
|
|||||||
from synapse.logging.context import LoggingContext
|
from synapse.logging.context import LoggingContext
|
||||||
from synapse.metrics import METRICS_PREFIX, MetricsResource, RegistryProxy
|
from synapse.metrics import METRICS_PREFIX, MetricsResource, RegistryProxy
|
||||||
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
|
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
|
||||||
from synapse.rest import ClientRestResource
|
from synapse.rest import ClientRestResource, admin
|
||||||
from synapse.rest.admin import register_servlets_for_media_repo
|
|
||||||
from synapse.rest.health import HealthResource
|
from synapse.rest.health import HealthResource
|
||||||
from synapse.rest.key.v2 import KeyResource
|
from synapse.rest.key.v2 import KeyResource
|
||||||
from synapse.rest.synapse.client import build_synapse_client_resource_tree
|
from synapse.rest.synapse.client import build_synapse_client_resource_tree
|
||||||
@ -176,8 +175,13 @@ class GenericWorkerServer(HomeServer):
|
|||||||
def _listen_http(self, listener_config: ListenerConfig) -> None:
|
def _listen_http(self, listener_config: ListenerConfig) -> None:
|
||||||
assert listener_config.http_options is not None
|
assert listener_config.http_options is not None
|
||||||
|
|
||||||
# We always include a health resource.
|
# We always include an admin resource that we populate with servlets as needed
|
||||||
resources: Dict[str, Resource] = {"/health": HealthResource()}
|
admin_resource = JsonResource(self, canonical_json=False)
|
||||||
|
resources: Dict[str, Resource] = {
|
||||||
|
# We always include a health resource.
|
||||||
|
"/health": HealthResource(),
|
||||||
|
"/_synapse/admin": admin_resource,
|
||||||
|
}
|
||||||
|
|
||||||
for res in listener_config.http_options.resources:
|
for res in listener_config.http_options.resources:
|
||||||
for name in res.names:
|
for name in res.names:
|
||||||
@ -190,6 +194,7 @@ class GenericWorkerServer(HomeServer):
|
|||||||
|
|
||||||
resources.update(build_synapse_client_resource_tree(self))
|
resources.update(build_synapse_client_resource_tree(self))
|
||||||
resources["/.well-known"] = well_known_resource(self)
|
resources["/.well-known"] = well_known_resource(self)
|
||||||
|
admin.register_servlets(self, admin_resource)
|
||||||
|
|
||||||
elif name == "federation":
|
elif name == "federation":
|
||||||
resources[FEDERATION_PREFIX] = TransportLayerServer(self)
|
resources[FEDERATION_PREFIX] = TransportLayerServer(self)
|
||||||
@ -199,15 +204,13 @@ class GenericWorkerServer(HomeServer):
|
|||||||
|
|
||||||
# We need to serve the admin servlets for media on the
|
# We need to serve the admin servlets for media on the
|
||||||
# worker.
|
# worker.
|
||||||
admin_resource = JsonResource(self, canonical_json=False)
|
admin.register_servlets_for_media_repo(self, admin_resource)
|
||||||
register_servlets_for_media_repo(self, admin_resource)
|
|
||||||
|
|
||||||
resources.update(
|
resources.update(
|
||||||
{
|
{
|
||||||
MEDIA_R0_PREFIX: media_repo,
|
MEDIA_R0_PREFIX: media_repo,
|
||||||
MEDIA_V3_PREFIX: media_repo,
|
MEDIA_V3_PREFIX: media_repo,
|
||||||
LEGACY_MEDIA_PREFIX: media_repo,
|
LEGACY_MEDIA_PREFIX: media_repo,
|
||||||
"/_synapse/admin": admin_resource,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -284,8 +287,7 @@ class GenericWorkerServer(HomeServer):
|
|||||||
elif listener.type == "metrics":
|
elif listener.type == "metrics":
|
||||||
if not self.config.metrics.enable_metrics:
|
if not self.config.metrics.enable_metrics:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Metrics listener configured, but "
|
"Metrics listener configured, but enable_metrics is not True!"
|
||||||
"enable_metrics is not True!"
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if isinstance(listener, TCPListenerConfig):
|
if isinstance(listener, TCPListenerConfig):
|
||||||
|
@ -54,6 +54,7 @@ from synapse.config.server import ListenerConfig, TCPListenerConfig
|
|||||||
from synapse.federation.transport.server import TransportLayerServer
|
from synapse.federation.transport.server import TransportLayerServer
|
||||||
from synapse.http.additional_resource import AdditionalResource
|
from synapse.http.additional_resource import AdditionalResource
|
||||||
from synapse.http.server import (
|
from synapse.http.server import (
|
||||||
|
JsonResource,
|
||||||
OptionsResource,
|
OptionsResource,
|
||||||
RootOptionsRedirectResource,
|
RootOptionsRedirectResource,
|
||||||
StaticResource,
|
StaticResource,
|
||||||
@ -61,8 +62,7 @@ from synapse.http.server import (
|
|||||||
from synapse.logging.context import LoggingContext
|
from synapse.logging.context import LoggingContext
|
||||||
from synapse.metrics import METRICS_PREFIX, MetricsResource, RegistryProxy
|
from synapse.metrics import METRICS_PREFIX, MetricsResource, RegistryProxy
|
||||||
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
|
from synapse.replication.http import REPLICATION_PREFIX, ReplicationRestResource
|
||||||
from synapse.rest import ClientRestResource
|
from synapse.rest import ClientRestResource, admin
|
||||||
from synapse.rest.admin import AdminRestResource
|
|
||||||
from synapse.rest.health import HealthResource
|
from synapse.rest.health import HealthResource
|
||||||
from synapse.rest.key.v2 import KeyResource
|
from synapse.rest.key.v2 import KeyResource
|
||||||
from synapse.rest.synapse.client import build_synapse_client_resource_tree
|
from synapse.rest.synapse.client import build_synapse_client_resource_tree
|
||||||
@ -180,11 +180,14 @@ class SynapseHomeServer(HomeServer):
|
|||||||
if compress:
|
if compress:
|
||||||
client_resource = gz_wrap(client_resource)
|
client_resource = gz_wrap(client_resource)
|
||||||
|
|
||||||
|
admin_resource = JsonResource(self, canonical_json=False)
|
||||||
|
admin.register_servlets(self, admin_resource)
|
||||||
|
|
||||||
resources.update(
|
resources.update(
|
||||||
{
|
{
|
||||||
CLIENT_API_PREFIX: client_resource,
|
CLIENT_API_PREFIX: client_resource,
|
||||||
"/.well-known": well_known_resource(self),
|
"/.well-known": well_known_resource(self),
|
||||||
"/_synapse/admin": AdminRestResource(self),
|
"/_synapse/admin": admin_resource,
|
||||||
**build_synapse_client_resource_tree(self),
|
**build_synapse_client_resource_tree(self),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -286,8 +289,7 @@ class SynapseHomeServer(HomeServer):
|
|||||||
elif listener.type == "metrics":
|
elif listener.type == "metrics":
|
||||||
if not self.config.metrics.enable_metrics:
|
if not self.config.metrics.enable_metrics:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Metrics listener configured, but "
|
"Metrics listener configured, but enable_metrics is not True!"
|
||||||
"enable_metrics is not True!"
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if isinstance(listener, TCPListenerConfig):
|
if isinstance(listener, TCPListenerConfig):
|
||||||
|
@ -28,12 +28,26 @@ from prometheus_client import Gauge
|
|||||||
|
|
||||||
from synapse.metrics.background_process_metrics import wrap_as_background_process
|
from synapse.metrics.background_process_metrics import wrap_as_background_process
|
||||||
from synapse.types import JsonDict
|
from synapse.types import JsonDict
|
||||||
|
from synapse.util.constants import ONE_HOUR_SECONDS, ONE_MINUTE_SECONDS
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
|
||||||
logger = logging.getLogger("synapse.app.homeserver")
|
logger = logging.getLogger("synapse.app.homeserver")
|
||||||
|
|
||||||
|
MILLISECONDS_PER_SECOND = 1000
|
||||||
|
|
||||||
|
INITIAL_DELAY_BEFORE_FIRST_PHONE_HOME_SECONDS = 5 * ONE_MINUTE_SECONDS
|
||||||
|
"""
|
||||||
|
We wait 5 minutes to send the first set of stats as the server can be quite busy the
|
||||||
|
first few minutes
|
||||||
|
"""
|
||||||
|
|
||||||
|
PHONE_HOME_INTERVAL_SECONDS = 3 * ONE_HOUR_SECONDS
|
||||||
|
"""
|
||||||
|
Phone home stats are sent every 3 hours
|
||||||
|
"""
|
||||||
|
|
||||||
# Contains the list of processes we will be monitoring
|
# Contains the list of processes we will be monitoring
|
||||||
# currently either 0 or 1
|
# currently either 0 or 1
|
||||||
_stats_process: List[Tuple[int, "resource.struct_rusage"]] = []
|
_stats_process: List[Tuple[int, "resource.struct_rusage"]] = []
|
||||||
@ -157,7 +171,7 @@ async def phone_stats_home(
|
|||||||
stats["log_level"] = logging.getLevelName(log_level)
|
stats["log_level"] = logging.getLevelName(log_level)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Reporting stats to %s: %s" % (hs.config.metrics.report_stats_endpoint, stats)
|
"Reporting stats to %s: %s", hs.config.metrics.report_stats_endpoint, stats
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
await hs.get_proxied_http_client().put_json(
|
await hs.get_proxied_http_client().put_json(
|
||||||
@ -185,12 +199,14 @@ def start_phone_stats_home(hs: "HomeServer") -> None:
|
|||||||
# If you increase the loop period, the accuracy of user_daily_visits
|
# If you increase the loop period, the accuracy of user_daily_visits
|
||||||
# table will decrease
|
# table will decrease
|
||||||
clock.looping_call(
|
clock.looping_call(
|
||||||
hs.get_datastores().main.generate_user_daily_visits, 5 * 60 * 1000
|
hs.get_datastores().main.generate_user_daily_visits,
|
||||||
|
5 * ONE_MINUTE_SECONDS * MILLISECONDS_PER_SECOND,
|
||||||
)
|
)
|
||||||
|
|
||||||
# monthly active user limiting functionality
|
# monthly active user limiting functionality
|
||||||
clock.looping_call(
|
clock.looping_call(
|
||||||
hs.get_datastores().main.reap_monthly_active_users, 1000 * 60 * 60
|
hs.get_datastores().main.reap_monthly_active_users,
|
||||||
|
ONE_HOUR_SECONDS * MILLISECONDS_PER_SECOND,
|
||||||
)
|
)
|
||||||
hs.get_datastores().main.reap_monthly_active_users()
|
hs.get_datastores().main.reap_monthly_active_users()
|
||||||
|
|
||||||
@ -221,7 +237,12 @@ def start_phone_stats_home(hs: "HomeServer") -> None:
|
|||||||
|
|
||||||
if hs.config.metrics.report_stats:
|
if hs.config.metrics.report_stats:
|
||||||
logger.info("Scheduling stats reporting for 3 hour intervals")
|
logger.info("Scheduling stats reporting for 3 hour intervals")
|
||||||
clock.looping_call(phone_stats_home, 3 * 60 * 60 * 1000, hs, stats)
|
clock.looping_call(
|
||||||
|
phone_stats_home,
|
||||||
|
PHONE_HOME_INTERVAL_SECONDS * MILLISECONDS_PER_SECOND,
|
||||||
|
hs,
|
||||||
|
stats,
|
||||||
|
)
|
||||||
|
|
||||||
# We need to defer this init for the cases that we daemonize
|
# We need to defer this init for the cases that we daemonize
|
||||||
# otherwise the process ID we get is that of the non-daemon process
|
# otherwise the process ID we get is that of the non-daemon process
|
||||||
@ -229,4 +250,6 @@ def start_phone_stats_home(hs: "HomeServer") -> None:
|
|||||||
|
|
||||||
# We wait 5 minutes to send the first set of stats as the server can
|
# We wait 5 minutes to send the first set of stats as the server can
|
||||||
# be quite busy the first few minutes
|
# be quite busy the first few minutes
|
||||||
clock.call_later(5 * 60, phone_stats_home, hs, stats)
|
clock.call_later(
|
||||||
|
INITIAL_DELAY_BEFORE_FIRST_PHONE_HOME_SECONDS, phone_stats_home, hs, stats
|
||||||
|
)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||||
#
|
#
|
||||||
# Copyright 2015, 2016 OpenMarket Ltd
|
# Copyright 2015, 2016 OpenMarket Ltd
|
||||||
# Copyright (C) 2023 New Vector, Ltd
|
# Copyright (C) 2023, 2025 New Vector, Ltd
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
@ -70,6 +70,8 @@ from typing import (
|
|||||||
Tuple,
|
Tuple,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from twisted.internet.interfaces import IDelayedCall
|
||||||
|
|
||||||
from synapse.appservice import (
|
from synapse.appservice import (
|
||||||
ApplicationService,
|
ApplicationService,
|
||||||
ApplicationServiceState,
|
ApplicationServiceState,
|
||||||
@ -450,6 +452,20 @@ class _TransactionController:
|
|||||||
recoverer.recover()
|
recoverer.recover()
|
||||||
logger.info("Now %i active recoverers", len(self.recoverers))
|
logger.info("Now %i active recoverers", len(self.recoverers))
|
||||||
|
|
||||||
|
def force_retry(self, service: ApplicationService) -> None:
|
||||||
|
"""Forces a Recoverer to attempt delivery of transations immediately.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
service:
|
||||||
|
"""
|
||||||
|
recoverer = self.recoverers.get(service.id)
|
||||||
|
if not recoverer:
|
||||||
|
# No need to force a retry on a happy AS.
|
||||||
|
logger.info("%s is not in recovery, not forcing retry", service.id)
|
||||||
|
return
|
||||||
|
|
||||||
|
recoverer.force_retry()
|
||||||
|
|
||||||
async def _is_service_up(self, service: ApplicationService) -> bool:
|
async def _is_service_up(self, service: ApplicationService) -> bool:
|
||||||
state = await self.store.get_appservice_state(service)
|
state = await self.store.get_appservice_state(service)
|
||||||
return state == ApplicationServiceState.UP or state is None
|
return state == ApplicationServiceState.UP or state is None
|
||||||
@ -482,11 +498,12 @@ class _Recoverer:
|
|||||||
self.service = service
|
self.service = service
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
self.backoff_counter = 1
|
self.backoff_counter = 1
|
||||||
|
self.scheduled_recovery: Optional[IDelayedCall] = None
|
||||||
|
|
||||||
def recover(self) -> None:
|
def recover(self) -> None:
|
||||||
delay = 2**self.backoff_counter
|
delay = 2**self.backoff_counter
|
||||||
logger.info("Scheduling retries on %s in %fs", self.service.id, delay)
|
logger.info("Scheduling retries on %s in %fs", self.service.id, delay)
|
||||||
self.clock.call_later(
|
self.scheduled_recovery = self.clock.call_later(
|
||||||
delay, run_as_background_process, "as-recoverer", self.retry
|
delay, run_as_background_process, "as-recoverer", self.retry
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -496,6 +513,21 @@ class _Recoverer:
|
|||||||
self.backoff_counter += 1
|
self.backoff_counter += 1
|
||||||
self.recover()
|
self.recover()
|
||||||
|
|
||||||
|
def force_retry(self) -> None:
|
||||||
|
"""Cancels the existing timer and forces an immediate retry in the background.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
service:
|
||||||
|
"""
|
||||||
|
# Prevent the existing backoff from occuring
|
||||||
|
if self.scheduled_recovery:
|
||||||
|
self.clock.cancel_call_later(self.scheduled_recovery)
|
||||||
|
# Run a retry, which will resechedule a recovery if it fails.
|
||||||
|
run_as_background_process(
|
||||||
|
"retry",
|
||||||
|
self.retry,
|
||||||
|
)
|
||||||
|
|
||||||
async def retry(self) -> None:
|
async def retry(self) -> None:
|
||||||
logger.info("Starting retries on %s", self.service.id)
|
logger.info("Starting retries on %s", self.service.id)
|
||||||
try:
|
try:
|
||||||
|
@ -170,7 +170,7 @@ class Config:
|
|||||||
|
|
||||||
section: ClassVar[str]
|
section: ClassVar[str]
|
||||||
|
|
||||||
def __init__(self, root_config: "RootConfig" = None):
|
def __init__(self, root_config: "RootConfig"):
|
||||||
self.root = root_config
|
self.root = root_config
|
||||||
|
|
||||||
# Get the path to the default Synapse template directory
|
# Get the path to the default Synapse template directory
|
||||||
@ -445,7 +445,7 @@ class RootConfig:
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def invoke_all_static(cls, func_name: str, *args: Any, **kwargs: any) -> None:
|
def invoke_all_static(cls, func_name: str, *args: Any, **kwargs: Any) -> None:
|
||||||
"""
|
"""
|
||||||
Invoke a static function on config objects this RootConfig is
|
Invoke a static function on config objects this RootConfig is
|
||||||
configured to use.
|
configured to use.
|
||||||
@ -1047,7 +1047,7 @@ class RoutableShardedWorkerHandlingConfig(ShardedWorkerHandlingConfig):
|
|||||||
return self._get_instance(key)
|
return self._get_instance(key)
|
||||||
|
|
||||||
|
|
||||||
def read_file(file_path: Any, config_path: Iterable[str]) -> str:
|
def read_file(file_path: Any, config_path: StrSequence) -> str:
|
||||||
"""Check the given file exists, and read it into a string
|
"""Check the given file exists, and read it into a string
|
||||||
|
|
||||||
If it does not, emit an error indicating the problem
|
If it does not, emit an error indicating the problem
|
||||||
|
@ -59,6 +59,7 @@ from synapse.config import ( # noqa: F401
|
|||||||
tls,
|
tls,
|
||||||
tracer,
|
tracer,
|
||||||
user_directory,
|
user_directory,
|
||||||
|
user_types,
|
||||||
voip,
|
voip,
|
||||||
workers,
|
workers,
|
||||||
)
|
)
|
||||||
@ -122,6 +123,7 @@ class RootConfig:
|
|||||||
retention: retention.RetentionConfig
|
retention: retention.RetentionConfig
|
||||||
background_updates: background_updates.BackgroundUpdateConfig
|
background_updates: background_updates.BackgroundUpdateConfig
|
||||||
auto_accept_invites: auto_accept_invites.AutoAcceptInvitesConfig
|
auto_accept_invites: auto_accept_invites.AutoAcceptInvitesConfig
|
||||||
|
user_types: user_types.UserTypesConfig
|
||||||
|
|
||||||
config_classes: List[Type["Config"]] = ...
|
config_classes: List[Type["Config"]] = ...
|
||||||
config_files: List[str]
|
config_files: List[str]
|
||||||
@ -179,7 +181,7 @@ class RootConfig:
|
|||||||
class Config:
|
class Config:
|
||||||
root: RootConfig
|
root: RootConfig
|
||||||
default_template_dir: str
|
default_template_dir: str
|
||||||
def __init__(self, root_config: Optional[RootConfig] = ...) -> None: ...
|
def __init__(self, root_config: RootConfig = ...) -> None: ...
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_size(value: Union[str, int]) -> int: ...
|
def parse_size(value: Union[str, int]) -> int: ...
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -212,4 +214,4 @@ class ShardedWorkerHandlingConfig:
|
|||||||
class RoutableShardedWorkerHandlingConfig(ShardedWorkerHandlingConfig):
|
class RoutableShardedWorkerHandlingConfig(ShardedWorkerHandlingConfig):
|
||||||
def get_instance(self, key: str) -> str: ... # noqa: F811
|
def get_instance(self, key: str) -> str: ... # noqa: F811
|
||||||
|
|
||||||
def read_file(file_path: Any, config_path: Iterable[str]) -> str: ...
|
def read_file(file_path: Any, config_path: StrSequence) -> str: ...
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
import enum
|
import enum
|
||||||
from functools import cache
|
from functools import cache
|
||||||
from typing import TYPE_CHECKING, Any, Iterable, Optional
|
from typing import TYPE_CHECKING, Any, Optional
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
import attr.validators
|
import attr.validators
|
||||||
@ -29,7 +29,7 @@ import attr.validators
|
|||||||
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersions
|
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS, RoomVersions
|
||||||
from synapse.config import ConfigError
|
from synapse.config import ConfigError
|
||||||
from synapse.config._base import Config, RootConfig, read_file
|
from synapse.config._base import Config, RootConfig, read_file
|
||||||
from synapse.types import JsonDict
|
from synapse.types import JsonDict, StrSequence
|
||||||
|
|
||||||
# Determine whether authlib is installed.
|
# Determine whether authlib is installed.
|
||||||
try:
|
try:
|
||||||
@ -45,7 +45,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def read_secret_from_file_once(file_path: Any, config_path: Iterable[str]) -> str:
|
def read_secret_from_file_once(file_path: Any, config_path: StrSequence) -> str:
|
||||||
"""Returns the memoized secret read from file."""
|
"""Returns the memoized secret read from file."""
|
||||||
return read_file(file_path, config_path).strip()
|
return read_file(file_path, config_path).strip()
|
||||||
|
|
||||||
@ -560,3 +560,18 @@ class ExperimentalConfig(Config):
|
|||||||
|
|
||||||
# MSC4076: Add `disable_badge_count`` to pusher configuration
|
# MSC4076: Add `disable_badge_count`` to pusher configuration
|
||||||
self.msc4076_enabled: bool = experimental.get("msc4076_enabled", False)
|
self.msc4076_enabled: bool = experimental.get("msc4076_enabled", False)
|
||||||
|
|
||||||
|
# MSC4235: Add `via` param to hierarchy endpoint
|
||||||
|
self.msc4235_enabled: bool = experimental.get("msc4235_enabled", False)
|
||||||
|
|
||||||
|
# MSC4263: Preventing MXID enumeration via key queries
|
||||||
|
self.msc4263_limit_key_queries_to_users_who_share_rooms = experimental.get(
|
||||||
|
"msc4263_limit_key_queries_to_users_who_share_rooms",
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# MSC4267: Automatically forgetting rooms on leave
|
||||||
|
self.msc4267_enabled: bool = experimental.get("msc4267_enabled", False)
|
||||||
|
|
||||||
|
# MSC4155: Invite filtering
|
||||||
|
self.msc4155_enabled: bool = experimental.get("msc4155_enabled", False)
|
||||||
|
@ -94,5 +94,21 @@ class FederationConfig(Config):
|
|||||||
2**62,
|
2**62,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def is_domain_allowed_according_to_federation_whitelist(self, domain: str) -> bool:
|
||||||
|
"""
|
||||||
|
Returns whether a domain is allowed according to the federation whitelist. If a
|
||||||
|
federation whitelist is not set, all domains are allowed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
domain: The domain to test.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the domain is allowed or if a whitelist is not set, False otherwise.
|
||||||
|
"""
|
||||||
|
if self.federation_domain_whitelist is None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return domain in self.federation_domain_whitelist
|
||||||
|
|
||||||
|
|
||||||
_METRICS_FOR_DOMAINS_SCHEMA = {"type": "array", "items": {"type": "string"}}
|
_METRICS_FOR_DOMAINS_SCHEMA = {"type": "array", "items": {"type": "string"}}
|
||||||
|
@ -59,6 +59,7 @@ from .third_party_event_rules import ThirdPartyRulesConfig
|
|||||||
from .tls import TlsConfig
|
from .tls import TlsConfig
|
||||||
from .tracer import TracerConfig
|
from .tracer import TracerConfig
|
||||||
from .user_directory import UserDirectoryConfig
|
from .user_directory import UserDirectoryConfig
|
||||||
|
from .user_types import UserTypesConfig
|
||||||
from .voip import VoipConfig
|
from .voip import VoipConfig
|
||||||
from .workers import WorkerConfig
|
from .workers import WorkerConfig
|
||||||
|
|
||||||
@ -107,4 +108,5 @@ class HomeServerConfig(RootConfig):
|
|||||||
ExperimentalConfig,
|
ExperimentalConfig,
|
||||||
BackgroundUpdateConfig,
|
BackgroundUpdateConfig,
|
||||||
AutoAcceptInvitesConfig,
|
AutoAcceptInvitesConfig,
|
||||||
|
UserTypesConfig,
|
||||||
]
|
]
|
||||||
|
@ -191,7 +191,7 @@ class KeyConfig(Config):
|
|||||||
if macaroon_secret_key:
|
if macaroon_secret_key:
|
||||||
raise ConfigError(CONFLICTING_MACAROON_SECRET_KEY_OPTS_ERROR)
|
raise ConfigError(CONFLICTING_MACAROON_SECRET_KEY_OPTS_ERROR)
|
||||||
macaroon_secret_key = read_file(
|
macaroon_secret_key = read_file(
|
||||||
macaroon_secret_key_path, "macaroon_secret_key_path"
|
macaroon_secret_key_path, ("macaroon_secret_key_path",)
|
||||||
).strip()
|
).strip()
|
||||||
if not macaroon_secret_key:
|
if not macaroon_secret_key:
|
||||||
macaroon_secret_key = self.root.registration.registration_shared_secret
|
macaroon_secret_key = self.root.registration.registration_shared_secret
|
||||||
@ -216,7 +216,9 @@ class KeyConfig(Config):
|
|||||||
if form_secret_path:
|
if form_secret_path:
|
||||||
if form_secret:
|
if form_secret:
|
||||||
raise ConfigError(CONFLICTING_FORM_SECRET_OPTS_ERROR)
|
raise ConfigError(CONFLICTING_FORM_SECRET_OPTS_ERROR)
|
||||||
self.form_secret = read_file(form_secret_path, "form_secret_path").strip()
|
self.form_secret = read_file(
|
||||||
|
form_secret_path, ("form_secret_path",)
|
||||||
|
).strip()
|
||||||
else:
|
else:
|
||||||
self.form_secret = form_secret
|
self.form_secret = form_secret
|
||||||
|
|
||||||
|
@ -51,6 +51,8 @@ if TYPE_CHECKING:
|
|||||||
from synapse.config.homeserver import HomeServerConfig
|
from synapse.config.homeserver import HomeServerConfig
|
||||||
from synapse.server import HomeServer
|
from synapse.server import HomeServer
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEFAULT_LOG_CONFIG = Template(
|
DEFAULT_LOG_CONFIG = Template(
|
||||||
"""\
|
"""\
|
||||||
# Log configuration for Synapse.
|
# Log configuration for Synapse.
|
||||||
@ -291,7 +293,7 @@ def _load_logging_config(log_config_path: str) -> None:
|
|||||||
log_config = yaml.safe_load(f.read())
|
log_config = yaml.safe_load(f.read())
|
||||||
|
|
||||||
if not log_config:
|
if not log_config:
|
||||||
logging.warning("Loaded a blank logging config?")
|
logger.warning("Loaded a blank logging config?")
|
||||||
|
|
||||||
# If the old structured logging configuration is being used, raise an error.
|
# If the old structured logging configuration is being used, raise an error.
|
||||||
if "structured" in log_config and log_config.get("structured"):
|
if "structured" in log_config and log_config.get("structured"):
|
||||||
@ -312,7 +314,7 @@ def _reload_logging_config(log_config_path: Optional[str]) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
_load_logging_config(log_config_path)
|
_load_logging_config(log_config_path)
|
||||||
logging.info("Reloaded log config from %s due to SIGHUP", log_config_path)
|
logger.info("Reloaded log config from %s due to SIGHUP", log_config_path)
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(
|
def setup_logging(
|
||||||
@ -349,17 +351,17 @@ def setup_logging(
|
|||||||
appbase.register_sighup(_reload_logging_config, log_config_path)
|
appbase.register_sighup(_reload_logging_config, log_config_path)
|
||||||
|
|
||||||
# Log immediately so we can grep backwards.
|
# Log immediately so we can grep backwards.
|
||||||
logging.warning("***** STARTING SERVER *****")
|
logger.warning("***** STARTING SERVER *****")
|
||||||
logging.warning(
|
logger.warning(
|
||||||
"Server %s version %s",
|
"Server %s version %s",
|
||||||
sys.argv[0],
|
sys.argv[0],
|
||||||
SYNAPSE_VERSION,
|
SYNAPSE_VERSION,
|
||||||
)
|
)
|
||||||
logging.warning("Copyright (c) 2023 New Vector, Inc")
|
logger.warning("Copyright (c) 2023 New Vector, Inc")
|
||||||
logging.warning(
|
logger.warning(
|
||||||
"Licensed under the AGPL 3.0 license. Website: https://github.com/element-hq/synapse"
|
"Licensed under the AGPL 3.0 license. Website: https://github.com/element-hq/synapse"
|
||||||
)
|
)
|
||||||
logging.info("Server hostname: %s", config.server.server_name)
|
logger.info("Server hostname: %s", config.server.server_name)
|
||||||
logging.info("Public Base URL: %s", config.server.public_baseurl)
|
logger.info("Public Base URL: %s", config.server.public_baseurl)
|
||||||
logging.info("Instance name: %s", hs.get_instance_name())
|
logger.info("Instance name: %s", hs.get_instance_name())
|
||||||
logging.info("Twisted reactor: %s", type(reactor).__name__)
|
logger.info("Twisted reactor: %s", type(reactor).__name__)
|
||||||
|
@ -356,6 +356,9 @@ def _parse_oidc_config_dict(
|
|||||||
additional_authorization_parameters=oidc_config.get(
|
additional_authorization_parameters=oidc_config.get(
|
||||||
"additional_authorization_parameters", {}
|
"additional_authorization_parameters", {}
|
||||||
),
|
),
|
||||||
|
passthrough_authorization_parameters=oidc_config.get(
|
||||||
|
"passthrough_authorization_parameters", []
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -501,3 +504,6 @@ class OidcProviderConfig:
|
|||||||
|
|
||||||
# Additional parameters that will be passed to the authorization grant URL
|
# Additional parameters that will be passed to the authorization grant URL
|
||||||
additional_authorization_parameters: Mapping[str, str]
|
additional_authorization_parameters: Mapping[str, str]
|
||||||
|
|
||||||
|
# Allow query parameters to the redirect endpoint that will be passed to the authorization grant URL
|
||||||
|
passthrough_authorization_parameters: Collection[str]
|
||||||
|
@ -240,3 +240,9 @@ class RatelimitConfig(Config):
|
|||||||
"rc_delayed_event_mgmt",
|
"rc_delayed_event_mgmt",
|
||||||
defaults={"per_second": 1, "burst_count": 5},
|
defaults={"per_second": 1, "burst_count": 5},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.rc_reports = RatelimitSettings.parse(
|
||||||
|
config,
|
||||||
|
"rc_reports",
|
||||||
|
defaults={"per_second": 1, "burst_count": 5},
|
||||||
|
)
|
||||||
|
@ -162,6 +162,10 @@ class RegistrationConfig(Config):
|
|||||||
"disable_msisdn_registration", False
|
"disable_msisdn_registration", False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.allow_underscore_prefixed_localpart = config.get(
|
||||||
|
"allow_underscore_prefixed_localpart", False
|
||||||
|
)
|
||||||
|
|
||||||
session_lifetime = config.get("session_lifetime")
|
session_lifetime = config.get("session_lifetime")
|
||||||
if session_lifetime is not None:
|
if session_lifetime is not None:
|
||||||
session_lifetime = self.parse_duration(session_lifetime)
|
session_lifetime = self.parse_duration(session_lifetime)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user