1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
5401
5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
5473
5474
5475
5476
5477
5478
5479
5480
5481
5482
5483
5484
5485
5486
5487
5488
5489
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
5517
5518
5519
5520
5521
5522
5523
5524
5525
5526
5527
5528
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543
5544
5545
5546
5547
5548
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789
5790
5791
5792
5793
5794
5795
5796
5797
5798
5799
5800
5801
5802
5803
5804
5805
5806
5807
5808
5809
5810
5811
5812
5813
5814
5815
5816
5817
5818
5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842
5843
5844
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
5896
5897
5898
5899
5900
5901
5902
5903
5904
5905
5906
5907
5908
5909
5910
5911
5912
5913
5914
5915
5916
5917
5918
5919
5920
5921
5922
5923
5924
5925
5926
5927
5928
5929
5930
5931
5932
5933
5934
5935
5936
5937
5938
5939
5940
5941
5942
5943
5944
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
5960
5961
5962
5963
5964
5965
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
5997
5998
5999
6000
6001
6002
6003
6004
6005
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
6021
6022
6023
6024
6025
6026
6027
6028
6029
6030
6031
6032
6033
6034
6035
6036
6037
6038
6039
6040
6041
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053
6054
6055
6056
6057
6058
6059
6060
6061
6062
6063
6064
6065
6066
6067
6068
6069
6070
6071
6072
6073
6074
6075
6076
6077
6078
6079
6080
6081
6082
6083
6084
6085
6086
6087
6088
6089
6090
6091
6092
6093
6094
6095
6096
6097
6098
6099
6100
6101
6102
6103
6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
6140
6141
6142
6143
6144
6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
6161
6162
6163
6164
6165
6166
6167
6168
6169
6170
6171
6172
6173
6174
6175
6176
6177
6178
6179
6180
6181
6182
6183
6184
6185
6186
6187
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
6211
6212
6213
6214
6215
6216
6217
6218
6219
6220
6221
6222
6223
6224
6225
6226
6227
6228
6229
6230
6231
6232
6233
6234
6235
6236
6237
6238
6239
6240
6241
6242
6243
6244
6245
6246
6247
6248
6249
6250
6251
6252
6253
6254
6255
6256
6257
6258
6259
6260
6261
6262
6263
6264
6265
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
6279
6280
6281
6282
6283
6284
6285
6286
6287
6288
6289
6290
6291
6292
6293
6294
6295
6296
6297
6298
6299
6300
6301
6302
6303
6304
6305
6306
6307
6308
6309
6310
6311
6312
6313
6314
6315
6316
6317
6318
6319
6320
6321
6322
6323
6324
6325
6326
6327
6328
6329
6330
6331
6332
6333
6334
6335
6336
6337
6338
6339
6340
6341
6342
6343
6344
6345
6346
6347
6348
6349
6350
6351
6352
6353
6354
6355
6356
6357
6358
6359
6360
6361
6362
6363
6364
6365
6366
6367
6368
6369
6370
6371
6372
6373
6374
6375
6376
6377
6378
6379
6380
6381
6382
6383
6384
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
6414
6415
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
6435
6436
6437
6438
6439
6440
6441
6442
6443
6444
6445
6446
6447
6448
6449
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
6465
6466
6467
6468
6469
6470
6471
6472
6473
6474
6475
6476
6477
6478
6479
6480
6481
6482
6483
6484
6485
6486
6487
6488
6489
6490
6491
6492
6493
6494
6495
6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
6508
6509
6510
6511
6512
6513
6514
6515
6516
6517
6518
6519
6520
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534
6535
6536
6537
6538
6539
6540
6541
6542
6543
6544
6545
6546
6547
6548
6549
6550
6551
6552
6553
6554
6555
6556
6557
6558
6559
6560
6561
6562
6563
6564
6565
6566
6567
6568
6569
6570
6571
6572
6573
6574
6575
6576
6577
6578
6579
6580
6581
6582
6583
6584
6585
6586
6587
6588
6589
6590
6591
6592
6593
6594
6595
6596
6597
6598
6599
6600
6601
6602
6603
6604
6605
6606
6607
6608
6609
6610
6611
6612
6613
6614
6615
6616
6617
6618
6619
6620
6621
6622
6623
6624
6625
6626
6627
6628
6629
6630
6631
6632
6633
6634
6635
6636
6637
6638
6639
6640
6641
6642
6643
6644
6645
6646
6647
6648
6649
6650
6651
6652
6653
6654
6655
6656
6657
6658
6659
6660
6661
6662
6663
6664
6665
6666
6667
6668
6669
6670
6671
6672
6673
6674
6675
6676
6677
6678
6679
6680
6681
6682
6683
6684
6685
6686
6687
6688
6689
6690
6691
6692
6693
6694
6695
6696
6697
6698
6699
6700
6701
6702
6703
6704
6705
6706
6707
6708
6709
6710
6711
6712
6713
6714
6715
6716
6717
6718
6719
6720
6721
6722
6723
6724
6725
6726
6727
6728
6729
6730
6731
6732
6733
6734
6735
6736
6737
6738
6739
6740
6741
6742
6743
6744
6745
6746
6747
6748
6749
6750
6751
6752
6753
6754
6755
6756
6757
6758
6759
6760
6761
6762
6763
6764
6765
6766
6767
6768
6769
6770
6771
6772
6773
6774
6775
6776
6777
6778
6779
6780
6781
6782
6783
6784
6785
6786
6787
6788
6789
6790
6791
6792
6793
6794
6795
6796
6797
6798
6799
6800
6801
6802
6803
6804
6805
6806
6807
6808
6809
6810
6811
6812
6813
6814
6815
6816
6817
6818
6819
6820
6821
6822
6823
6824
6825
6826
6827
6828
6829
6830
6831
6832
6833
6834
6835
6836
6837
6838
6839
6840
6841
6842
6843
6844
6845
6846
6847
6848
6849
6850
6851
6852
6853
6854
6855
6856
6857
6858
6859
6860
6861
6862
6863
6864
6865
6866
6867
6868
6869
6870
6871
6872
6873
6874
6875
6876
6877
6878
6879
6880
6881
6882
6883
6884
6885
6886
6887
6888
6889
6890
6891
6892
6893
6894
6895
6896
6897
6898
6899
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910
6911
6912
6913
6914
6915
6916
6917
6918
6919
6920
6921
6922
6923
6924
6925
6926
6927
6928
6929
6930
6931
6932
6933
6934
6935
6936
6937
6938
6939
6940
6941
6942
6943
6944
6945
6946
6947
6948
6949
6950
6951
6952
6953
6954
6955
6956
6957
6958
6959
6960
6961
6962
6963
6964
6965
6966
6967
6968
6969
6970
6971
6972
6973
6974
6975
6976
6977
6978
6979
6980
6981
6982
6983
6984
6985
6986
6987
6988
6989
6990
6991
6992
6993
6994
6995
6996
6997
6998
6999
7000
7001
7002
7003
7004
7005
7006
7007
7008
7009
7010
7011
7012
7013
7014
7015
7016
7017
7018
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032
7033
7034
7035
7036
7037
7038
7039
7040
7041
7042
7043
7044
7045
7046
7047
7048
7049
7050
7051
7052
7053
7054
7055
7056
7057
7058
7059
7060
7061
7062
7063
7064
7065
7066
7067
7068
7069
7070
7071
7072
7073
7074
7075
7076
7077
7078
7079
7080
7081
7082
7083
7084
7085
7086
7087
7088
7089
7090
7091
7092
7093
7094
7095
7096
7097
7098
7099
7100
7101
7102
7103
7104
7105
7106
7107
7108
7109
7110
7111
7112
7113
7114
7115
7116
7117
7118
7119
7120
7121
7122
7123
7124
7125
7126
7127
7128
7129
7130
7131
7132
7133
7134
7135
7136
7137
7138
7139
7140
7141
7142
7143
7144
7145
7146
7147
7148
7149
7150
7151
7152
7153
7154
7155
7156
7157
7158
7159
7160
7161
7162
7163
7164
7165
7166
7167
7168
7169
7170
7171
7172
7173
7174
7175
7176
7177
7178
7179
7180
7181
7182
7183
7184
7185
7186
7187
7188
7189
7190
7191
7192
7193
7194
7195
7196
7197
7198
7199
7200
7201
7202
7203
7204
7205
7206
7207
7208
7209
7210
7211
7212
7213
7214
7215
7216
7217
7218
7219
7220
7221
7222
7223
7224
7225
7226
7227
7228
7229
7230
7231
7232
7233
7234
7235
7236
7237
7238
7239
7240
7241
7242
7243
7244
7245
7246
7247
7248
7249
7250
7251
7252
7253
7254
7255
7256
7257
7258
7259
7260
7261
7262
7263
7264
7265
7266
7267
7268
7269
7270
7271
7272
7273
7274
7275
7276
7277
7278
7279
7280
7281
7282
7283
7284
7285
7286
7287
7288
7289
7290
7291
7292
7293
7294
7295
7296
7297
7298
7299
7300
7301
7302
7303
7304
7305
7306
7307
7308
7309
7310
7311
7312
7313
7314
7315
7316
7317
7318
7319
7320
7321
7322
7323
7324
7325
7326
7327
7328
7329
7330
7331
7332
7333
7334
7335
7336
7337
7338
7339
7340
7341
7342
7343
7344
7345
7346
7347
7348
7349
7350
7351
7352
7353
7354
7355
7356
7357
7358
7359
7360
7361
7362
7363
7364
7365
7366
7367
7368
7369
7370
7371
7372
7373
7374
7375
7376
7377
7378
7379
7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
7395
7396
7397
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
7414
7415
7416
7417
7418
7419
7420
7421
7422
7423
7424
7425
7426
7427
7428
7429
7430
7431
7432
7433
7434
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
7452
7453
7454
7455
7456
7457
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
7479
7480
7481
7482
7483
7484
7485
7486
7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
7497
7498
7499
7500
7501
7502
7503
7504
7505
7506
7507
7508
7509
7510
7511
7512
7513
7514
7515
7516
7517
7518
7519
7520
7521
7522
7523
7524
7525
7526
7527
7528
7529
7530
7531
7532
7533
7534
7535
7536
7537
7538
7539
7540
7541
7542
7543
7544
7545
7546
7547
7548
7549
7550
7551
7552
7553
7554
7555
7556
7557
7558
7559
7560
7561
7562
7563
7564
7565
7566
7567
7568
7569
7570
7571
7572
7573
7574
7575
7576
7577
7578
7579
7580
7581
7582
7583
7584
7585
7586
7587
7588
7589
7590
7591
7592
7593
7594
7595
7596
7597
7598
7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616
7617
7618
7619
7620
7621
7622
7623
7624
7625
7626
7627
7628
7629
7630
7631
7632
7633
7634
7635
7636
7637
7638
7639
7640
7641
7642
7643
7644
7645
7646
7647
7648
7649
7650
7651
7652
7653
7654
7655
7656
7657
7658
7659
7660
7661
7662
7663
7664
7665
7666
7667
7668
7669
7670
7671
7672
7673
7674
7675
7676
7677
7678
7679
7680
7681
7682
7683
7684
7685
7686
7687
7688
7689
7690
7691
7692
7693
7694
7695
7696
7697
7698
7699
7700
7701
7702
7703
7704
7705
7706
7707
7708
7709
7710
7711
7712
7713
7714
7715
7716
7717
7718
7719
7720
7721
7722
7723
7724
7725
7726
7727
7728
7729
7730
7731
7732
7733
7734
7735
7736
7737
7738
7739
7740
7741
7742
7743
7744
7745
7746
7747
7748
7749
7750
7751
7752
7753
7754
7755
7756
7757
7758
7759
7760
7761
7762
7763
7764
7765
7766
7767
7768
7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
7781
7782
7783
7784
7785
7786
7787
7788
7789
7790
7791
7792
7793
7794
7795
7796
7797
7798
7799
7800
7801
7802
7803
7804
7805
7806
7807
7808
7809
7810
7811
7812
7813
7814
7815
7816
7817
7818
7819
7820
7821
7822
7823
7824
7825
7826
7827
7828
7829
7830
7831
7832
7833
7834
7835
7836
7837
7838
7839
7840
7841
7842
7843
7844
7845
7846
7847
7848
7849
7850
7851
7852
7853
7854
7855
7856
7857
7858
7859
7860
7861
7862
7863
7864
7865
7866
7867
7868
7869
7870
7871
7872
7873
7874
7875
7876
7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
7897
7898
7899
7900
7901
7902
7903
7904
7905
7906
7907
7908
7909
7910
7911
7912
7913
7914
7915
7916
7917
7918
7919
7920
7921
7922
7923
7924
7925
7926
7927
7928
7929
7930
7931
7932
7933
7934
7935
7936
7937
7938
7939
7940
7941
7942
7943
7944
7945
7946
7947
7948
7949
7950
7951
7952
7953
7954
7955
7956
7957
7958
7959
7960
7961
7962
7963
7964
7965
7966
7967
7968
7969
7970
7971
7972
7973
7974
7975
7976
7977
7978
7979
7980
7981
7982
7983
7984
7985
7986
7987
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
8003
8004
8005
8006
8007
8008
8009
8010
8011
8012
8013
8014
8015
8016
8017
8018
8019
8020
8021
8022
8023
8024
8025
8026
8027
8028
8029
8030
8031
8032
8033
8034
8035
8036
8037
8038
8039
8040
8041
8042
8043
8044
8045
8046
8047
8048
8049
8050
8051
8052
8053
8054
8055
8056
8057
8058
8059
8060
8061
8062
8063
8064
8065
8066
8067
8068
8069
8070
8071
8072
8073
8074
8075
8076
8077
8078
8079
8080
8081
8082
8083
8084
8085
8086
8087
8088
8089
8090
8091
8092
8093
8094
8095
8096
8097
8098
8099
8100
8101
8102
8103
8104
8105
8106
8107
8108
8109
8110
8111
8112
8113
8114
8115
8116
8117
8118
8119
8120
8121
8122
8123
8124
8125
8126
8127
8128
8129
8130
8131
8132
8133
8134
8135
8136
8137
8138
8139
8140
8141
8142
8143
8144
8145
8146
8147
8148
8149
8150
8151
8152
8153
8154
8155
8156
8157
8158
8159
8160
8161
8162
8163
8164
8165
8166
8167
8168
8169
8170
8171
8172
8173
8174
8175
8176
8177
8178
8179
8180
8181
8182
8183
8184
8185
8186
8187
8188
8189
8190
8191
8192
8193
8194
8195
8196
8197
8198
8199
8200
8201
8202
8203
8204
8205
8206
8207
8208
8209
8210
8211
8212
8213
8214
8215
8216
8217
8218
8219
8220
8221
8222
8223
8224
8225
8226
8227
8228
8229
8230
8231
8232
8233
8234
8235
8236
8237
8238
8239
8240
8241
8242
8243
8244
8245
8246
8247
8248
8249
8250
8251
8252
8253
8254
8255
8256
8257
8258
8259
8260
8261
8262
8263
8264
8265
8266
8267
8268
8269
8270
8271
8272
8273
8274
8275
8276
8277
8278
8279
8280
8281
8282
8283
8284
8285
8286
8287
8288
8289
8290
8291
8292
8293
8294
8295
8296
8297
8298
8299
8300
8301
8302
8303
8304
8305
8306
8307
8308
8309
8310
8311
8312
8313
8314
8315
8316
8317
8318
8319
8320
8321
8322
8323
8324
8325
8326
8327
8328
8329
8330
8331
8332
8333
8334
8335
8336
8337
8338
8339
8340
8341
8342
8343
8344
8345
8346
8347
8348
8349
8350
8351
8352
8353
8354
8355
8356
8357
8358
8359
8360
8361
8362
8363
8364
8365
8366
8367
8368
8369
8370
8371
8372
8373
8374
8375
8376
8377
8378
8379
8380
8381
8382
8383
8384
8385
8386
8387
8388
8389
8390
8391
8392
8393
8394
8395
8396
8397
8398
8399
8400
8401
8402
8403
8404
8405
8406
8407
8408
8409
8410
8411
8412
8413
8414
8415
8416
8417
8418
8419
8420
8421
8422
8423
8424
8425
8426
8427
8428
8429
8430
8431
8432
8433
8434
8435
8436
8437
8438
8439
8440
8441
8442
8443
8444
8445
8446
8447
8448
8449
8450
8451
8452
8453
8454
8455
8456
8457
8458
8459
8460
8461
8462
8463
8464
8465
8466
8467
8468
8469
8470
8471
8472
8473
8474
8475
8476
8477
8478
8479
8480
8481
8482
8483
8484
8485
8486
8487
8488
8489
8490
8491
8492
8493
8494
8495
8496
8497
8498
8499
8500
8501
8502
8503
8504
8505
8506
8507
8508
8509
8510
8511
8512
8513
8514
8515
8516
8517
8518
8519
8520
8521
8522
8523
8524
8525
8526
8527
8528
8529
8530
8531
8532
8533
8534
8535
8536
8537
8538
8539
8540
8541
8542
8543
8544
8545
8546
8547
8548
8549
8550
8551
8552
8553
8554
8555
8556
8557
8558
8559
8560
8561
8562
8563
8564
8565
8566
8567
8568
8569
8570
8571
8572
8573
8574
8575
8576
8577
8578
8579
8580
8581
8582
8583
8584
8585
8586
8587
8588
8589
8590
8591
8592
8593
8594
8595
8596
8597
8598
8599
8600
8601
8602
8603
8604
8605
8606
8607
8608
8609
8610
8611
8612
8613
8614
8615
8616
8617
8618
8619
8620
8621
8622
8623
8624
8625
8626
8627
8628
8629
8630
8631
8632
8633
8634
8635
8636
8637
8638
8639
8640
8641
8642
8643
8644
8645
8646
8647
8648
8649
8650
8651
8652
8653
8654
8655
8656
8657
8658
8659
8660
8661
8662
8663
8664
8665
8666
8667
8668
8669
8670
8671
8672
8673
8674
8675
8676
8677
8678
8679
8680
8681
8682
8683
8684
8685
8686
8687
8688
8689
8690
8691
8692
8693
8694
8695
8696
8697
8698
8699
8700
8701
8702
8703
8704
8705
8706
8707
8708
8709
8710
8711
8712
8713
8714
8715
8716
8717
8718
8719
8720
8721
8722
8723
8724
8725
8726
8727
8728
8729
8730
8731
8732
8733
8734
8735
8736
8737
8738
8739
8740
8741
8742
8743
8744
8745
8746
8747
8748
8749
8750
8751
8752
8753
8754
8755
8756
8757
8758
8759
8760
8761
8762
8763
8764
8765
8766
8767
8768
8769
8770
8771
8772
8773
8774
8775
8776
8777
8778
8779
8780
8781
8782
8783
8784
8785
8786
8787
8788
8789
8790
8791
8792
8793
8794
8795
8796
8797
8798
8799
8800
8801
8802
8803
8804
8805
8806
8807
8808
8809
8810
8811
8812
8813
8814
8815
8816
8817
8818
8819
8820
8821
8822
8823
8824
8825
8826
8827
8828
8829
8830
8831
8832
8833
8834
8835
8836
8837
8838
8839
8840
8841
8842
8843
8844
8845
8846
8847
8848
8849
8850
8851
8852
8853
8854
8855
8856
8857
8858
8859
8860
8861
8862
8863
8864
8865
8866
8867
8868
8869
8870
8871
8872
8873
8874
8875
8876
8877
8878
8879
8880
8881
8882
8883
8884
8885
8886
8887
8888
8889
8890
8891
8892
8893
8894
8895
8896
8897
8898
8899
8900
8901
8902
8903
8904
8905
8906
8907
8908
8909
8910
8911
8912
8913
8914
8915
8916
8917
8918
8919
8920
8921
8922
8923
8924
8925
8926
8927
8928
8929
8930
8931
8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943
8944
8945
8946
8947
8948
8949
8950
8951
8952
8953
8954
8955
8956
8957
8958
8959
8960
8961
8962
8963
8964
8965
8966
8967
8968
8969
8970
8971
8972
8973
8974
8975
8976
8977
8978
8979
8980
8981
8982
8983
8984
8985
8986
8987
8988
8989
8990
8991
8992
8993
8994
8995
8996
8997
8998
8999
9000
9001
9002
9003
9004
9005
9006
9007
9008
9009
9010
9011
9012
9013
9014
9015
9016
9017
9018
9019
9020
9021
9022
9023
9024
9025
9026
9027
9028
9029
9030
9031
9032
9033
9034
9035
9036
9037
9038
9039
9040
9041
9042
9043
9044
9045
9046
9047
9048
9049
9050
9051
9052
9053
9054
9055
9056
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067
9068
9069
9070
9071
9072
9073
9074
9075
9076
9077
9078
9079
9080
9081
9082
9083
9084
9085
9086
9087
9088
9089
9090
9091
9092
9093
9094
9095
9096
9097
9098
9099
9100
9101
9102
9103
9104
9105
9106
9107
9108
9109
9110
9111
9112
9113
9114
9115
9116
9117
9118
9119
9120
9121
9122
9123
9124
9125
9126
9127
9128
9129
9130
9131
9132
9133
9134
9135
9136
9137
9138
9139
9140
9141
9142
9143
9144
9145
9146
9147
9148
9149
9150
9151
9152
9153
9154
9155
9156
9157
9158
9159
9160
9161
9162
9163
9164
9165
9166
9167
9168
9169
9170
9171
9172
9173
9174
9175
9176
9177
9178
9179
9180
9181
9182
9183
9184
9185
9186
9187
9188
9189
9190
9191
9192
9193
9194
9195
9196
9197
9198
9199
9200
9201
9202
9203
9204
9205
9206
9207
9208
9209
9210
9211
9212
9213
9214
9215
9216
9217
9218
9219
9220
9221
9222
9223
9224
9225
9226
9227
9228
9229
9230
9231
9232
9233
9234
9235
9236
9237
9238
9239
9240
9241
9242
9243
9244
9245
9246
9247
9248
9249
9250
9251
9252
9253
9254
9255
9256
9257
9258
9259
9260
9261
9262
9263
9264
9265
9266
9267
9268
9269
9270
9271
9272
9273
9274
9275
9276
9277
9278
9279
9280
9281
9282
9283
9284
9285
9286
9287
9288
9289
9290
9291
9292
9293
9294
9295
9296
9297
9298
9299
9300
9301
9302
9303
9304
9305
9306
9307
9308
9309
9310
9311
9312
9313
9314
9315
9316
9317
9318
9319
9320
9321
9322
9323
9324
9325
9326
9327
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
9338
9339
9340
9341
9342
9343
9344
9345
9346
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
9358
9359
9360
9361
9362
9363
9364
9365
9366
9367
9368
9369
9370
9371
9372
9373
9374
9375
9376
9377
9378
9379
9380
9381
9382
9383
9384
9385
9386
9387
9388
9389
9390
9391
9392
9393
9394
9395
9396
9397
9398
9399
9400
9401
9402
9403
9404
9405
9406
9407
9408
9409
9410
9411
9412
9413
9414
9415
9416
9417
9418
9419
9420
9421
9422
9423
9424
9425
9426
9427
9428
9429
9430
9431
9432
9433
9434
9435
9436
9437
9438
9439
9440
9441
9442
9443
9444
9445
9446
9447
9448
9449
9450
9451
9452
9453
9454
9455
9456
9457
9458
9459
9460
9461
9462
9463
9464
9465
9466
9467
9468
9469
9470
9471
9472
9473
9474
9475
9476
9477
9478
9479
9480
9481
9482
9483
9484
9485
9486
9487
9488
9489
9490
9491
9492
9493
9494
9495
9496
9497
9498
9499
9500
9501
9502
9503
9504
9505
9506
9507
9508
9509
9510
9511
9512
9513
9514
9515
9516
9517
9518
9519
9520
9521
9522
9523
9524
9525
9526
9527
9528
9529
9530
9531
9532
9533
9534
9535
9536
9537
9538
9539
9540
9541
9542
9543
9544
9545
9546
9547
9548
9549
9550
9551
9552
9553
9554
9555
9556
9557
9558
9559
9560
9561
9562
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
9576
9577
9578
9579
9580
9581
9582
9583
9584
9585
9586
9587
9588
9589
9590
9591
9592
9593
9594
9595
9596
9597
9598
9599
9600
9601
9602
9603
9604
9605
9606
9607
9608
9609
9610
9611
9612
9613
9614
9615
9616
9617
9618
9619
9620
9621
9622
9623
9624
9625
9626
9627
9628
9629
9630
9631
9632
9633
9634
9635
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645
9646
9647
9648
9649
9650
9651
9652
9653
9654
9655
9656
9657
9658
9659
9660
9661
9662
9663
9664
9665
9666
9667
9668
9669
9670
9671
9672
9673
9674
9675
9676
9677
9678
9679
9680
9681
9682
9683
9684
9685
9686
9687
9688
9689
9690
9691
9692
9693
9694
9695
9696
9697
9698
9699
9700
9701
9702
9703
9704
9705
9706
9707
9708
9709
9710
9711
9712
9713
9714
9715
9716
9717
9718
9719
9720
9721
9722
9723
9724
9725
9726
9727
9728
9729
9730
9731
9732
9733
9734
9735
9736
9737
9738
9739
9740
9741
9742
9743
9744
9745
9746
9747
9748
9749
9750
9751
9752
9753
9754
9755
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770
9771
9772
9773
9774
9775
9776
9777
9778
9779
9780
9781
9782
9783
9784
9785
9786
9787
9788
9789
9790
9791
9792
9793
9794
9795
9796
9797
9798
9799
9800
9801
9802
9803
9804
9805
9806
9807
9808
9809
9810
9811
9812
9813
9814
9815
9816
9817
9818
9819
9820
9821
9822
9823
9824
9825
9826
9827
9828
9829
9830
9831
9832
9833
9834
9835
9836
9837
9838
9839
9840
9841
9842
9843
9844
9845
9846
9847
9848
9849
9850
9851
9852
9853
9854
9855
9856
9857
9858
9859
9860
9861
9862
9863
9864
9865
9866
9867
9868
9869
9870
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886
9887
9888
9889
9890
9891
9892
9893
9894
9895
9896
9897
9898
9899
9900
9901
9902
9903
9904
9905
9906
9907
9908
9909
9910
9911
9912
9913
9914
9915
9916
9917
9918
9919
9920
9921
9922
9923
9924
9925
9926
9927
9928
9929
9930
9931
9932
9933
9934
9935
9936
9937
9938
9939
9940
9941
9942
9943
9944
9945
9946
9947
9948
9949
9950
9951
9952
9953
9954
9955
9956
9957
9958
9959
9960
9961
9962
9963
9964
9965
9966
9967
9968
9969
9970
9971
9972
9973
9974
9975
9976
9977
9978
9979
9980
9981
9982
9983
9984
9985
9986
9987
9988
9989
9990
9991
9992
9993
9994
9995
9996
9997
9998
9999
10000
10001
10002
10003
10004
10005
10006
10007
10008
10009
10010
10011
10012
10013
10014
10015
10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030
10031
10032
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046
10047
10048
10049
10050
10051
10052
10053
10054
10055
10056
10057
10058
10059
10060
10061
10062
10063
10064
10065
10066
10067
10068
10069
10070
10071
10072
10073
10074
10075
10076
10077
10078
10079
10080
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095
10096
10097
10098
10099
10100
10101
10102
10103
10104
10105
10106
10107
10108
10109
10110
10111
10112
10113
10114
10115
10116
10117
10118
10119
10120
10121
10122
10123
10124
10125
10126
10127
10128
10129
10130
10131
10132
10133
10134
10135
10136
10137
10138
10139
10140
10141
10142
10143
10144
10145
10146
10147
10148
10149
10150
10151
10152
10153
10154
10155
10156
10157
10158
10159
10160
10161
10162
10163
10164
10165
10166
10167
10168
10169
10170
10171
10172
10173
10174
10175
10176
10177
10178
10179
10180
10181
10182
10183
10184
10185
10186
10187
10188
10189
10190
10191
10192
10193
10194
10195
10196
10197
10198
10199
10200
10201
10202
10203
10204
10205
10206
10207
10208
10209
10210
10211
10212
10213
10214
10215
10216
10217
10218
10219
10220
10221
10222
10223
10224
10225
10226
10227
10228
10229
10230
10231
10232
10233
10234
10235
10236
10237
10238
10239
10240
10241
10242
10243
10244
10245
10246
10247
10248
10249
10250
10251
10252
10253
10254
10255
10256
10257
10258
10259
10260
10261
10262
10263
10264
10265
10266
10267
10268
10269
10270
10271
10272
10273
10274
10275
10276
10277
10278
10279
10280
10281
10282
10283
10284
10285
10286
10287
10288
10289
10290
10291
10292
10293
10294
10295
10296
10297
10298
10299
10300
10301
10302
10303
10304
10305
10306
10307
10308
10309
10310
10311
10312
10313
10314
10315
10316
10317
10318
10319
10320
10321
10322
10323
10324
10325
10326
10327
10328
10329
10330
10331
10332
10333
10334
10335
10336
10337
10338
10339
10340
10341
10342
10343
10344
10345
10346
10347
10348
10349
10350
10351
10352
10353
10354
10355
10356
10357
10358
10359
10360
10361
10362
10363
10364
10365
10366
10367
10368
10369
10370
10371
10372
10373
10374
10375
10376
10377
10378
10379
10380
10381
10382
10383
10384
10385
10386
10387
10388
10389
10390
10391
10392
10393
10394
10395
10396
10397
10398
10399
10400
10401
10402
10403
10404
10405
10406
10407
10408
10409
10410
10411
10412
10413
10414
10415
10416
10417
10418
10419
10420
10421
10422
10423
10424
10425
10426
10427
10428
10429
10430
10431
10432
10433
10434
10435
10436
10437
10438
10439
10440
10441
10442
10443
10444
10445
10446
10447
10448
10449
10450
10451
10452
10453
10454
10455
10456
10457
10458
10459
10460
10461
10462
10463
10464
10465
10466
10467
10468
10469
10470
10471
10472
10473
10474
10475
10476
10477
10478
10479
10480
10481
10482
10483
10484
10485
10486
10487
10488
10489
10490
10491
10492
10493
10494
10495
10496
10497
10498
10499
10500
10501
10502
10503
10504
10505
10506
10507
10508
10509
10510
10511
10512
10513
10514
10515
10516
10517
10518
10519
10520
10521
10522
10523
10524
10525
10526
10527
10528
10529
10530
10531
10532
10533
10534
10535
10536
10537
10538
10539
10540
10541
10542
10543
10544
10545
10546
10547
10548
10549
10550
10551
10552
10553
10554
10555
10556
10557
10558
10559
10560
10561
10562
10563
10564
10565
10566
10567
10568
10569
10570
10571
10572
10573
10574
10575
10576
10577
10578
10579
10580
10581
10582
10583
10584
10585
10586
10587
10588
10589
10590
10591
10592
10593
10594
10595
10596
10597
10598
10599
10600
10601
10602
10603
10604
10605
10606
10607
10608
10609
10610
10611
10612
10613
10614
10615
10616
10617
10618
10619
10620
10621
10622
10623
10624
10625
10626
10627
10628
10629
10630
10631
10632
10633
10634
10635
10636
10637
10638
10639
10640
10641
10642
10643
10644
10645
10646
10647
10648
10649
10650
10651
10652
10653
10654
10655
10656
10657
10658
10659
10660
10661
10662
10663
10664
10665
10666
10667
10668
10669
10670
10671
10672
10673
10674
10675
10676
10677
10678
10679
10680
10681
10682
10683
10684
10685
10686
10687
10688
10689
10690
10691
10692
10693
10694
10695
10696
10697
10698
10699
10700
10701
10702
10703
10704
10705
10706
10707
10708
10709
10710
10711
10712
10713
10714
10715
10716
10717
10718
10719
10720
10721
10722
10723
10724
10725
10726
10727
10728
10729
10730
10731
10732
10733
10734
10735
10736
10737
10738
10739
10740
10741
10742
10743
10744
10745
10746
10747
10748
10749
10750
10751
10752
10753
10754
10755
10756
10757
10758
10759
10760
10761
10762
10763
10764
10765
10766
10767
10768
10769
10770
10771
10772
10773
10774
10775
10776
10777
10778
10779
10780
10781
10782
10783
10784
10785
10786
10787
10788
10789
10790
10791
10792
10793
10794
10795
10796
10797
10798
10799
10800
10801
10802
10803
10804
10805
10806
10807
10808
10809
10810
10811
10812
10813
10814
10815
10816
10817
10818
10819
10820
10821
10822
10823
10824
10825
10826
10827
10828
10829
10830
10831
10832
10833
10834
10835
10836
10837
10838
10839
10840
10841
10842
10843
10844
10845
10846
10847
10848
10849
10850
10851
10852
10853
10854
10855
10856
10857
10858
10859
10860
10861
10862
10863
10864
10865
10866
10867
10868
10869
10870
10871
10872
10873
10874
10875
10876
10877
10878
10879
10880
10881
10882
10883
10884
10885
10886
10887
10888
10889
10890
10891
10892
10893
10894
10895
10896
10897
10898
10899
10900
10901
10902
10903
10904
10905
10906
10907
10908
10909
10910
10911
10912
10913
10914
10915
10916
10917
10918
10919
10920
10921
10922
10923
10924
10925
10926
10927
10928
10929
10930
10931
10932
10933
10934
10935
10936
10937
10938
10939
10940
10941
10942
10943
10944
10945
10946
10947
10948
10949
10950
10951
10952
10953
10954
10955
10956
10957
10958
10959
10960
10961
10962
10963
10964
10965
10966
10967
10968
10969
10970
10971
10972
10973
10974
10975
10976
10977
10978
10979
10980
10981
10982
10983
10984
10985
10986
10987
10988
10989
10990
10991
10992
10993
10994
10995
10996
10997
10998
10999
11000
11001
11002
11003
11004
11005
11006
11007
11008
11009
11010
11011
11012
11013
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026
11027
11028
11029
11030
11031
11032
11033
11034
11035
11036
11037
11038
11039
11040
11041
11042
11043
11044
11045
11046
11047
11048
11049
11050
11051
11052
11053
11054
11055
11056
11057
11058
11059
11060
11061
11062
11063
11064
11065
11066
11067
11068
11069
11070
11071
11072
11073
11074
11075
11076
11077
11078
11079
11080
11081
11082
11083
11084
11085
11086
11087
11088
11089
11090
11091
11092
11093
11094
11095
11096
11097
11098
11099
11100
11101
11102
11103
11104
11105
11106
11107
11108
11109
11110
11111
11112
11113
11114
11115
11116
11117
11118
11119
11120
11121
11122
11123
11124
11125
11126
11127
11128
11129
11130
11131
11132
11133
11134
11135
11136
11137
11138
11139
11140
11141
11142
11143
11144
11145
11146
11147
11148
11149
11150
11151
11152
11153
11154
11155
11156
11157
11158
11159
11160
11161
11162
11163
11164
11165
11166
11167
11168
11169
11170
11171
11172
11173
11174
11175
11176
11177
11178
11179
11180
11181
11182
11183
11184
11185
11186
11187
11188
11189
11190
11191
11192
11193
11194
11195
11196
11197
11198
11199
11200
11201
11202
11203
11204
11205
11206
11207
11208
11209
11210
11211
11212
11213
11214
11215
11216
11217
11218
11219
11220
11221
11222
11223
11224
11225
11226
11227
11228
11229
11230
11231
11232
11233
11234
11235
11236
11237
11238
11239
11240
11241
11242
11243
11244
11245
11246
11247
11248
11249
11250
11251
11252
11253
11254
11255
11256
11257
11258
11259
11260
11261
11262
11263
11264
11265
11266
11267
11268
11269
11270
11271
11272
11273
11274
11275
11276
11277
11278
11279
11280
11281
11282
11283
11284
11285
11286
11287
11288
11289
11290
11291
11292
11293
11294
11295
11296
11297
11298
11299
11300
11301
11302
11303
11304
11305
11306
11307
11308
11309
11310
11311
11312
11313
11314
11315
11316
11317
11318
11319
11320
11321
11322
11323
11324
11325
11326
11327
11328
11329
11330
11331
11332
11333
11334
11335
11336
11337
11338
11339
11340
11341
11342
11343
11344
11345
11346
11347
11348
11349
11350
11351
11352
11353
11354
11355
11356
11357
11358
11359
11360
11361
11362
11363
11364
11365
11366
11367
11368
11369
11370
11371
11372
11373
11374
11375
11376
11377
11378
11379
11380
11381
11382
11383
11384
11385
11386
11387
11388
11389
11390
11391
11392
11393
11394
11395
11396
11397
11398
11399
11400
11401
11402
11403
11404
11405
11406
11407
11408
11409
11410
11411
11412
11413
11414
11415
11416
11417
11418
11419
11420
11421
11422
11423
11424
11425
11426
11427
11428
11429
11430
11431
11432
11433
11434
11435
11436
11437
11438
11439
11440
11441
11442
11443
11444
11445
11446
11447
11448
11449
11450
11451
11452
11453
11454
11455
11456
11457
11458
11459
11460
11461
11462
11463
11464
11465
11466
11467
11468
11469
11470
11471
11472
11473
11474
11475
11476
11477
11478
11479
11480
11481
11482
11483
11484
11485
11486
11487
11488
11489
11490
11491
11492
11493
11494
11495
11496
11497
11498
11499
11500
11501
11502
11503
11504
11505
11506
11507
11508
11509
11510
11511
11512
11513
11514
11515
11516
11517
11518
11519
11520
11521
11522
11523
11524
11525
11526
11527
11528
11529
11530
11531
11532
11533
11534
11535
11536
11537
11538
11539
11540
11541
11542
11543
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553
11554
11555
11556
11557
11558
11559
11560
11561
11562
11563
11564
11565
11566
11567
11568
11569
11570
11571
11572
11573
11574
11575
11576
11577
11578
11579
11580
11581
11582
11583
11584
11585
11586
11587
11588
11589
11590
11591
11592
11593
11594
11595
11596
11597
11598
11599
11600
11601
11602
11603
11604
11605
11606
11607
11608
11609
11610
11611
11612
11613
11614
11615
11616
11617
11618
11619
11620
11621
11622
11623
11624
11625
11626
11627
11628
11629
11630
11631
11632
11633
11634
11635
11636
11637
11638
11639
11640
11641
11642
11643
11644
11645
11646
11647
11648
11649
11650
11651
11652
11653
11654
11655
11656
11657
11658
11659
11660
11661
11662
11663
11664
11665
11666
11667
11668
11669
11670
11671
11672
11673
11674
11675
11676
11677
11678
11679
11680
11681
11682
11683
11684
11685
11686
11687
11688
11689
11690
11691
11692
11693
11694
11695
11696
11697
11698
11699
11700
11701
11702
11703
11704
11705
11706
11707
11708
11709
11710
11711
11712
11713
11714
11715
11716
11717
11718
11719
11720
11721
11722
11723
11724
11725
11726
11727
11728
11729
11730
11731
11732
11733
11734
11735
11736
11737
11738
11739
11740
11741
11742
11743
11744
11745
11746
11747
11748
11749
11750
11751
11752
11753
11754
11755
11756
11757
11758
11759
11760
11761
11762
11763
11764
11765
11766
11767
11768
11769
11770
11771
11772
11773
11774
11775
11776
11777
11778
11779
11780
11781
11782
11783
11784
11785
11786
11787
11788
11789
11790
11791
11792
11793
11794
11795
11796
11797
11798
11799
11800
11801
11802
11803
11804
11805
11806
11807
11808
11809
11810
11811
11812
11813
11814
11815
11816
11817
11818
11819
11820
11821
11822
11823
11824
11825
11826
11827
11828
11829
11830
11831
11832
11833
11834
11835
11836
11837
11838
11839
11840
11841
11842
11843
11844
11845
11846
11847
11848
11849
11850
11851
11852
11853
11854
11855
11856
11857
11858
11859
11860
11861
11862
11863
11864
11865
11866
11867
11868
11869
11870
11871
11872
11873
11874
11875
11876
11877
11878
11879
11880
11881
11882
11883
11884
11885
11886
11887
11888
11889
11890
11891
11892
11893
11894
11895
11896
11897
11898
11899
11900
11901
11902
11903
11904
11905
11906
11907
11908
11909
11910
11911
11912
11913
11914
11915
11916
11917
11918
11919
11920
11921
11922
11923
11924
11925
11926
11927
11928
11929
11930
11931
11932
11933
11934
11935
11936
11937
11938
11939
11940
11941
11942
11943
11944
11945
11946
11947
11948
11949
11950
11951
11952
11953
11954
11955
11956
11957
11958
11959
11960
11961
11962
11963
11964
11965
11966
11967
11968
11969
11970
11971
11972
11973
11974
11975
11976
11977
11978
11979
11980
11981
11982
11983
11984
11985
11986
11987
11988
11989
11990
11991
11992
11993
11994
11995
11996
11997
11998
11999
12000
12001
12002
12003
12004
12005
12006
12007
12008
12009
12010
12011
12012
12013
12014
12015
12016
12017
12018
12019
12020
12021
12022
12023
12024
12025
12026
12027
12028
12029
12030
12031
12032
12033
12034
12035
12036
12037
12038
12039
12040
12041
12042
12043
12044
12045
12046
12047
12048
12049
12050
12051
12052
12053
12054
12055
12056
12057
12058
12059
12060
12061
12062
12063
12064
12065
12066
12067
12068
12069
12070
12071
12072
12073
12074
12075
12076
12077
12078
12079
12080
12081
12082
12083
12084
12085
12086
12087
12088
12089
12090
12091
12092
12093
12094
12095
12096
12097
12098
12099
12100
12101
12102
12103
12104
12105
12106
12107
12108
12109
12110
12111
12112
12113
12114
12115
12116
12117
12118
12119
12120
12121
12122
12123
12124
12125
12126
12127
12128
12129
12130
12131
12132
12133
12134
12135
12136
12137
12138
12139
12140
12141
12142
12143
12144
12145
12146
12147
12148
12149
12150
12151
12152
12153
12154
12155
12156
12157
12158
12159
12160
12161
12162
12163
12164
12165
12166
12167
12168
12169
12170
12171
12172
12173
12174
12175
12176
12177
12178
12179
12180
12181
12182
12183
12184
12185
12186
12187
12188
12189
12190
12191
12192
12193
12194
12195
12196
12197
12198
12199
12200
12201
12202
12203
12204
12205
12206
12207
12208
12209
12210
12211
12212
12213
12214
12215
12216
12217
12218
12219
12220
12221
12222
12223
12224
12225
12226
12227
12228
12229
12230
12231
12232
12233
12234
12235
12236
12237
12238
12239
12240
12241
12242
12243
12244
12245
12246
12247
12248
12249
12250
12251
12252
12253
12254
12255
12256
12257
12258
12259
12260
12261
12262
12263
12264
12265
12266
12267
12268
12269
12270
12271
12272
12273
12274
12275
12276
12277
12278
12279
12280
12281
12282
12283
12284
12285
12286
12287
12288
12289
12290
12291
12292
12293
12294
12295
12296
12297
12298
12299
12300
12301
12302
12303
12304
12305
12306
12307
12308
12309
12310
12311
12312
12313
12314
12315
12316
12317
12318
12319
12320
12321
12322
12323
12324
12325
12326
12327
12328
12329
12330
12331
12332
12333
12334
12335
12336
12337
12338
12339
12340
12341
12342
12343
12344
12345
12346
12347
12348
12349
12350
12351
12352
12353
12354
12355
12356
12357
12358
12359
12360
12361
12362
12363
12364
12365
12366
12367
12368
12369
12370
12371
12372
12373
12374
12375
12376
12377
12378
12379
12380
12381
12382
12383
12384
12385
12386
12387
12388
12389
12390
12391
12392
12393
12394
12395
12396
12397
12398
12399
12400
12401
12402
12403
12404
12405
12406
12407
12408
12409
12410
12411
12412
12413
12414
12415
12416
12417
12418
12419
12420
12421
12422
12423
12424
12425
12426
12427
12428
12429
12430
12431
12432
12433
12434
12435
12436
12437
12438
12439
12440
12441
12442
12443
12444
12445
12446
12447
12448
12449
12450
12451
12452
12453
12454
12455
12456
12457
12458
12459
12460
12461
12462
12463
12464
12465
12466
12467
12468
12469
12470
12471
12472
12473
12474
12475
12476
12477
12478
12479
12480
12481
12482
12483
12484
12485
12486
12487
12488
12489
12490
12491
12492
12493
12494
12495
12496
12497
12498
12499
12500
12501
12502
12503
12504
12505
12506
12507
12508
12509
12510
12511
12512
12513
12514
12515
12516
12517
12518
12519
12520
12521
12522
12523
12524
12525
12526
12527
12528
12529
12530
12531
12532
12533
12534
12535
12536
12537
12538
12539
12540
12541
12542
12543
12544
12545
12546
12547
12548
12549
12550
12551
12552
12553
12554
12555
12556
12557
12558
12559
12560
12561
12562
12563
12564
12565
12566
12567
12568
12569
12570
12571
12572
12573
12574
12575
12576
12577
12578
12579
12580
12581
12582
12583
12584
12585
12586
12587
12588
12589
12590
12591
12592
12593
12594
12595
12596
12597
12598
12599
12600
12601
12602
12603
12604
12605
12606
12607
12608
12609
12610
12611
12612
12613
12614
12615
12616
12617
12618
12619
12620
12621
12622
12623
12624
12625
12626
12627
12628
12629
12630
12631
12632
12633
12634
12635
12636
12637
12638
12639
12640
12641
12642
12643
12644
12645
12646
12647
12648
|
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Synapse</title>
<meta name="robots" content="noindex" />
<!-- Custom HTML head -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="docs/website_files/table-of-contents.css">
<link rel="stylesheet" href="docs/website_files/remove-nav-buttons.css">
<link rel="stylesheet" href="docs/website_files/indent-section-headers.css">
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><li class="part-title">Introduction</li><li class="chapter-item expanded "><a href="welcome_and_overview.html">Welcome and Overview</a></li><li class="chapter-item expanded affix "><li class="part-title">Setup</li><li class="chapter-item expanded "><a href="setup/installation.html">Installation</a></li><li class="chapter-item expanded "><a href="postgres.html">Using Postgres</a></li><li class="chapter-item expanded "><a href="reverse_proxy.html">Configuring a Reverse Proxy</a></li><li class="chapter-item expanded "><a href="turn-howto.html">Configuring a Turn Server</a></li><li class="chapter-item expanded "><a href="delegate.html">Delegation</a></li><li class="chapter-item expanded affix "><li class="part-title">Upgrading</li><li class="chapter-item expanded "><a href="upgrade.html">Upgrading between Synapse Versions</a></li><li class="chapter-item expanded "><a href="MSC1711_certificates_FAQ.html">Upgrading from pre-Synapse 1.0</a></li><li class="chapter-item expanded affix "><li class="part-title">Usage</li><li class="chapter-item expanded "><a href="federate.html">Federation</a></li><li class="chapter-item expanded "><a href="usage/configuration/index.html">Configuration</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="usage/configuration/homeserver_sample_config.html">Homeserver Sample Config File</a></li><li class="chapter-item expanded "><a href="usage/configuration/logging_sample_config.html">Logging Sample Config File</a></li><li class="chapter-item expanded "><a href="structured_logging.html">Structured Logging</a></li><li class="chapter-item expanded "><a href="usage/configuration/user_authentication/index.html">User Authentication</a></li><li><ol class="section"><li class="chapter-item expanded "><div>Single-Sign On</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="openid.html">OpenID Connect</a></li><li class="chapter-item expanded "><div>SAML</div></li><li class="chapter-item expanded "><div>CAS</div></li><li class="chapter-item expanded "><a href="sso_mapping_providers.html">SSO Mapping Providers</a></li></ol></li><li class="chapter-item expanded "><a href="password_auth_providers.html">Password Auth Providers</a></li><li class="chapter-item expanded "><a href="jwt.html">JSON Web Tokens</a></li></ol></li><li class="chapter-item expanded "><a href="CAPTCHA_SETUP.html">Registration Captcha</a></li><li class="chapter-item expanded "><a href="application_services.html">Application Services</a></li><li class="chapter-item expanded "><a href="server_notices.html">Server Notices</a></li><li class="chapter-item expanded "><a href="consent_tracking.html">Consent Tracking</a></li><li class="chapter-item expanded "><a href="url_previews.html">URL Previews</a></li><li class="chapter-item expanded "><a href="user_directory.html">User Directory</a></li><li class="chapter-item expanded "><a href="message_retention_policies.html">Message Retention Policies</a></li><li class="chapter-item expanded "><a href="modules.html">Pluggable Modules</a></li><li><ol class="section"><li class="chapter-item expanded "><div>Third Party Rules</div></li><li class="chapter-item expanded "><a href="spam_checker.html">Spam Checker</a></li><li class="chapter-item expanded "><a href="presence_router_module.html">Presence Router</a></li><li class="chapter-item expanded "><div>Media Storage Providers</div></li></ol></li><li class="chapter-item expanded "><a href="workers.html">Workers</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="synctl_workers.html">Using synctl with Workers</a></li><li class="chapter-item expanded "><a href="systemd-with-workers/index.html">Systemd</a></li></ol></li></ol></li><li class="chapter-item expanded "><a href="usage/administration/index.html">Administration</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="usage/administration/admin_api/index.html">Admin API</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="admin_api/account_validity.html">Account Validity</a></li><li class="chapter-item expanded "><a href="admin_api/delete_group.html">Delete Group</a></li><li class="chapter-item expanded "><a href="admin_api/event_reports.html">Event Reports</a></li><li class="chapter-item expanded "><a href="admin_api/media_admin_api.html">Media</a></li><li class="chapter-item expanded "><a href="admin_api/purge_history_api.html">Purge History</a></li><li class="chapter-item expanded "><a href="admin_api/purge_room.html">Purge Rooms</a></li><li class="chapter-item expanded "><a href="admin_api/register_api.html">Register Users</a></li><li class="chapter-item expanded "><a href="admin_api/room_membership.html">Manipulate Room Membership</a></li><li class="chapter-item expanded "><a href="admin_api/rooms.html">Rooms</a></li><li class="chapter-item expanded "><a href="admin_api/server_notices.html">Server Notices</a></li><li class="chapter-item expanded "><a href="admin_api/shutdown_room.html">Shutdown Room</a></li><li class="chapter-item expanded "><a href="admin_api/statistics.html">Statistics</a></li><li class="chapter-item expanded "><a href="admin_api/user_admin_api.html">Users</a></li><li class="chapter-item expanded "><a href="admin_api/version_api.html">Server Version</a></li></ol></li><li class="chapter-item expanded "><a href="manhole.html">Manhole</a></li><li class="chapter-item expanded "><a href="metrics-howto.html">Monitoring</a></li><li class="chapter-item expanded "><a href="usage/administration/request_log.html">Request log format</a></li><li class="chapter-item expanded "><div>Scripts</div></li></ol></li><li class="chapter-item expanded "><li class="part-title">Development</li><li class="chapter-item expanded "><a href="development/contributing_guide.html">Contributing Guide</a></li><li class="chapter-item expanded "><a href="code_style.html">Code Style</a></li><li class="chapter-item expanded "><a href="development/git.html">Git Usage</a></li><li class="chapter-item expanded "><div>Testing</div></li><li class="chapter-item expanded "><a href="opentracing.html">OpenTracing</a></li><li class="chapter-item expanded "><a href="development/database_schema.html">Database Schemas</a></li><li class="chapter-item expanded "><div>Synapse Architecture</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="log_contexts.html">Log Contexts</a></li><li class="chapter-item expanded "><a href="replication.html">Replication</a></li><li class="chapter-item expanded "><a href="tcp_replication.html">TCP Replication</a></li></ol></li><li class="chapter-item expanded "><a href="development/internal_documentation/index.html">Internal Documentation</a></li><li><ol class="section"><li class="chapter-item expanded "><div>Single Sign-On</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="development/saml.html">SAML</a></li><li class="chapter-item expanded "><a href="development/cas.html">CAS</a></li></ol></li><li class="chapter-item expanded "><div>State Resolution</div></li><li><ol class="section"><li class="chapter-item expanded "><a href="auth_chain_difference_algorithm.html">The Auth Chain Difference Algorithm</a></li></ol></li><li class="chapter-item expanded "><a href="media_repository.html">Media Repository</a></li><li class="chapter-item expanded "><a href="room_and_user_statistics.html">Room and User Statistics</a></li></ol></li><li class="chapter-item expanded "><div>Scripts</div></li><li class="chapter-item expanded affix "><li class="part-title">Other</li><li class="chapter-item expanded "><a href="deprecation_policy.html">Dependency Deprecation Policy</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Synapse</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/matrix-org/synapse" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<!-- Page table of contents -->
<div class="sidetoc">
<nav class="pagetoc"></nav>
</div>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="introduction"><a class="header" href="#introduction">Introduction</a></h1>
<p>Welcome to the documentation repository for Synapse, the reference
<a href="https://matrix.org">Matrix</a> homeserver implementation.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="installation-instructions"><a class="header" href="#installation-instructions">Installation Instructions</a></h1>
<p>There are 3 steps to follow under <strong>Installation Instructions</strong>.</p>
<ul>
<li><a href="setup/installation.html#installation-instructions">Installation Instructions</a>
<ul>
<li><a href="setup/installation.html#choosing-your-server-name">Choosing your server name</a></li>
<li><a href="setup/installation.html#installing-synapse">Installing Synapse</a>
<ul>
<li><a href="setup/installation.html#installing-from-source">Installing from source</a>
<ul>
<li><a href="setup/installation.html#platform-specific-prerequisites">Platform-specific prerequisites</a>
<ul>
<li><a href="setup/installation.html#debianubunturaspbian">Debian/Ubuntu/Raspbian</a></li>
<li><a href="setup/installation.html#archlinux">ArchLinux</a></li>
<li><a href="setup/installation.html#centosfedora">CentOS/Fedora</a></li>
<li><a href="setup/installation.html#macos">macOS</a></li>
<li><a href="setup/installation.html#opensuse">OpenSUSE</a></li>
<li><a href="setup/installation.html#openbsd">OpenBSD</a></li>
<li><a href="setup/installation.html#windows">Windows</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="setup/installation.html#prebuilt-packages">Prebuilt packages</a>
<ul>
<li><a href="setup/installation.html#docker-images-and-ansible-playbooks">Docker images and Ansible playbooks</a></li>
<li><a href="setup/installation.html#debianubuntu">Debian/Ubuntu</a>
<ul>
<li><a href="setup/installation.html#matrixorg-packages">Matrix.org packages</a></li>
<li><a href="setup/installation.html#downstream-debian-packages">Downstream Debian packages</a></li>
<li><a href="setup/installation.html#downstream-ubuntu-packages">Downstream Ubuntu packages</a></li>
</ul>
</li>
<li><a href="setup/installation.html#fedora">Fedora</a></li>
<li><a href="setup/installation.html#opensuse-1">OpenSUSE</a></li>
<li><a href="setup/installation.html#suse-linux-enterprise-server">SUSE Linux Enterprise Server</a></li>
<li><a href="setup/installation.html#archlinux-1">ArchLinux</a></li>
<li><a href="setup/installation.html#void-linux">Void Linux</a></li>
<li><a href="setup/installation.html#freebsd">FreeBSD</a></li>
<li><a href="setup/installation.html#openbsd-1">OpenBSD</a></li>
<li><a href="setup/installation.html#nixos">NixOS</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="setup/installation.html#setting-up-synapse">Setting up Synapse</a>
<ul>
<li><a href="setup/installation.html#using-postgresql">Using PostgreSQL</a></li>
<li><a href="setup/installation.html#tls-certificates">TLS certificates</a></li>
<li><a href="setup/installation.html#client-well-known-uri">Client Well-Known URI</a></li>
<li><a href="setup/installation.html#email">Email</a></li>
<li><a href="setup/installation.html#registering-a-user">Registering a user</a></li>
<li><a href="setup/installation.html#setting-up-a-turn-server">Setting up a TURN server</a></li>
<li><a href="setup/installation.html#url-previews">URL previews</a></li>
<li><a href="setup/installation.html#troubleshooting-installation">Troubleshooting Installation</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="choosing-your-server-name"><a class="header" href="#choosing-your-server-name">Choosing your server name</a></h2>
<p>It is important to choose the name for your server before you install Synapse,
because it cannot be changed later.</p>
<p>The server name determines the "domain" part of user-ids for users on your
server: these will all be of the format <code>@user:my.domain.name</code>. It also
determines how other matrix servers will reach yours for federation.</p>
<p>For a test configuration, set this to the hostname of your server. For a more
production-ready setup, you will probably want to specify your domain
(<code>example.com</code>) rather than a matrix-specific hostname here (in the same way
that your email address is probably <code>user@example.com</code> rather than
<code>user@email.example.com</code>) - but doing so may require more advanced setup: see
<a href="setup/../federate.html">Setting up Federation</a>.</p>
<h2 id="installing-synapse"><a class="header" href="#installing-synapse">Installing Synapse</a></h2>
<h3 id="installing-from-source"><a class="header" href="#installing-from-source">Installing from source</a></h3>
<p>(Prebuilt packages are available for some platforms - see <a href="setup/installation.html#prebuilt-packages">Prebuilt packages</a>.)</p>
<p>When installing from source please make sure that the <a href="setup/installation.html#platform-specific-prerequisites">Platform-specific prerequisites</a> are already installed.</p>
<p>System requirements:</p>
<ul>
<li>POSIX-compliant system (tested on Linux & OS X)</li>
<li>Python 3.5.2 or later, up to Python 3.9.</li>
<li>At least 1GB of free RAM if you want to join large public rooms like #matrix:matrix.org</li>
</ul>
<p>To install the Synapse homeserver run:</p>
<pre><code class="language-sh">mkdir -p ~/synapse
virtualenv -p python3 ~/synapse/env
source ~/synapse/env/bin/activate
pip install --upgrade pip
pip install --upgrade setuptools
pip install matrix-synapse
</code></pre>
<p>This will download Synapse from <a href="https://pypi.org/project/matrix-synapse">PyPI</a>
and install it, along with the python libraries it uses, into a virtual environment
under <code>~/synapse/env</code>. Feel free to pick a different directory if you
prefer.</p>
<p>This Synapse installation can then be later upgraded by using pip again with the
update flag:</p>
<pre><code class="language-sh">source ~/synapse/env/bin/activate
pip install -U matrix-synapse
</code></pre>
<p>Before you can start Synapse, you will need to generate a configuration
file. To do this, run (in your virtualenv, as before):</p>
<pre><code class="language-sh">cd ~/synapse
python -m synapse.app.homeserver \
--server-name my.domain.name \
--config-path homeserver.yaml \
--generate-config \
--report-stats=[yes|no]
</code></pre>
<p>... substituting an appropriate value for <code>--server-name</code>.</p>
<p>This command will generate you a config file that you can then customise, but it will
also generate a set of keys for you. These keys will allow your homeserver to
identify itself to other homeserver, so don't lose or delete them. It would be
wise to back them up somewhere safe. (If, for whatever reason, you do need to
change your homeserver's keys, you may find that other homeserver have the
old key cached. If you update the signing key, you should change the name of the
key in the <code><server name>.signing.key</code> file (the second word) to something
different. See the <a href="https://matrix.org/docs/spec/server_server/latest.html#retrieving-server-keys">spec</a> for more information on key management).</p>
<p>To actually run your new homeserver, pick a working directory for Synapse to
run (e.g. <code>~/synapse</code>), and:</p>
<pre><code class="language-sh">cd ~/synapse
source env/bin/activate
synctl start
</code></pre>
<h4 id="platform-specific-prerequisites"><a class="header" href="#platform-specific-prerequisites">Platform-specific prerequisites</a></h4>
<p>Synapse is written in Python but some of the libraries it uses are written in
C. So before we can install Synapse itself we need a working C compiler and the
header files for Python C extensions.</p>
<h5 id="debianubunturaspbian"><a class="header" href="#debianubunturaspbian">Debian/Ubuntu/Raspbian</a></h5>
<p>Installing prerequisites on Ubuntu or Debian:</p>
<pre><code class="language-sh">sudo apt install build-essential python3-dev libffi-dev \
python3-pip python3-setuptools sqlite3 \
libssl-dev virtualenv libjpeg-dev libxslt1-dev
</code></pre>
<h5 id="archlinux"><a class="header" href="#archlinux">ArchLinux</a></h5>
<p>Installing prerequisites on ArchLinux:</p>
<pre><code class="language-sh">sudo pacman -S base-devel python python-pip \
python-setuptools python-virtualenv sqlite3
</code></pre>
<h5 id="centosfedora"><a class="header" href="#centosfedora">CentOS/Fedora</a></h5>
<p>Installing prerequisites on CentOS or Fedora Linux:</p>
<pre><code class="language-sh">sudo dnf install libtiff-devel libjpeg-devel libzip-devel freetype-devel \
libwebp-devel libxml2-devel libxslt-devel libpq-devel \
python3-virtualenv libffi-devel openssl-devel python3-devel
sudo dnf groupinstall "Development Tools"
</code></pre>
<h5 id="macos"><a class="header" href="#macos">macOS</a></h5>
<p>Installing prerequisites on macOS:</p>
<p>You may need to install the latest Xcode developer tools:</p>
<pre><code class="language-sh">xcode-select --install
</code></pre>
<p>On ARM-based Macs you may need to explicitly install libjpeg which is a pillow dependency. You can use Homebrew (https://brew.sh):</p>
<pre><code class="language-sh"> brew install jpeg
</code></pre>
<p>On macOS Catalina (10.15) you may need to explicitly install OpenSSL
via brew and inform <code>pip</code> about it so that <code>psycopg2</code> builds:</p>
<pre><code class="language-sh">brew install openssl@1.1
export LDFLAGS="-L/usr/local/opt/openssl/lib"
export CPPFLAGS="-I/usr/local/opt/openssl/include"
</code></pre>
<h5 id="opensuse"><a class="header" href="#opensuse">OpenSUSE</a></h5>
<p>Installing prerequisites on openSUSE:</p>
<pre><code class="language-sh">sudo zypper in -t pattern devel_basis
sudo zypper in python-pip python-setuptools sqlite3 python-virtualenv \
python-devel libffi-devel libopenssl-devel libjpeg62-devel
</code></pre>
<h5 id="openbsd"><a class="header" href="#openbsd">OpenBSD</a></h5>
<p>A port of Synapse is available under <code>net/synapse</code>. The filesystem
underlying the homeserver directory (defaults to <code>/var/synapse</code>) has to be
mounted with <code>wxallowed</code> (cf. <code>mount(8)</code>), so creating a separate filesystem
and mounting it to <code>/var/synapse</code> should be taken into consideration.</p>
<p>To be able to build Synapse's dependency on python the <code>WRKOBJDIR</code>
(cf. <code>bsd.port.mk(5)</code>) for building python, too, needs to be on a filesystem
mounted with <code>wxallowed</code> (cf. <code>mount(8)</code>).</p>
<p>Creating a <code>WRKOBJDIR</code> for building python under <code>/usr/local</code> (which on a
default OpenBSD installation is mounted with <code>wxallowed</code>):</p>
<pre><code class="language-sh">doas mkdir /usr/local/pobj_wxallowed
</code></pre>
<p>Assuming <code>PORTS_PRIVSEP=Yes</code> (cf. <code>bsd.port.mk(5)</code>) and <code>SUDO=doas</code> are
configured in <code>/etc/mk.conf</code>:</p>
<pre><code class="language-sh">doas chown _pbuild:_pbuild /usr/local/pobj_wxallowed
</code></pre>
<p>Setting the <code>WRKOBJDIR</code> for building python:</p>
<pre><code class="language-sh">echo WRKOBJDIR_lang/python/3.7=/usr/local/pobj_wxallowed \\nWRKOBJDIR_lang/python/2.7=/usr/local/pobj_wxallowed >> /etc/mk.conf
</code></pre>
<p>Building Synapse:</p>
<pre><code class="language-sh">cd /usr/ports/net/synapse
make install
</code></pre>
<h5 id="windows"><a class="header" href="#windows">Windows</a></h5>
<p>If you wish to run or develop Synapse on Windows, the Windows Subsystem For
Linux provides a Linux environment on Windows 10 which is capable of using the
Debian, Fedora, or source installation methods. More information about WSL can
be found at <a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10">https://docs.microsoft.com/en-us/windows/wsl/install-win10</a> for
Windows 10 and <a href="https://docs.microsoft.com/en-us/windows/wsl/install-on-server">https://docs.microsoft.com/en-us/windows/wsl/install-on-server</a>
for Windows Server.</p>
<h3 id="prebuilt-packages"><a class="header" href="#prebuilt-packages">Prebuilt packages</a></h3>
<p>As an alternative to installing from source, prebuilt packages are available
for a number of platforms.</p>
<h4 id="docker-images-and-ansible-playbooks"><a class="header" href="#docker-images-and-ansible-playbooks">Docker images and Ansible playbooks</a></h4>
<p>There is an official synapse image available at
<a href="https://hub.docker.com/r/matrixdotorg/synapse">https://hub.docker.com/r/matrixdotorg/synapse</a> which can be used with
the docker-compose file available at
<a href="https://github.com/matrix-org/synapse/tree/develop/contrib/docker">contrib/docker</a>.
Further information on this including configuration options is available in the README
on hub.docker.com.</p>
<p>Alternatively, Andreas Peters (previously Silvio Fricke) has contributed a
Dockerfile to automate a synapse server in a single Docker image, at
<a href="https://hub.docker.com/r/avhost/docker-matrix/tags/">https://hub.docker.com/r/avhost/docker-matrix/tags/</a></p>
<p>Slavi Pantaleev has created an Ansible playbook,
which installs the offical Docker image of Matrix Synapse
along with many other Matrix-related services (Postgres database, Element, coturn,
ma1sd, SSL support, etc.).
For more details, see
<a href="https://github.com/spantaleev/matrix-docker-ansible-deploy">https://github.com/spantaleev/matrix-docker-ansible-deploy</a></p>
<h4 id="debianubuntu"><a class="header" href="#debianubuntu">Debian/Ubuntu</a></h4>
<h5 id="matrixorg-packages"><a class="header" href="#matrixorg-packages">Matrix.org packages</a></h5>
<p>Matrix.org provides Debian/Ubuntu packages of Synapse via
<a href="https://packages.matrix.org/debian/">https://packages.matrix.org/debian/</a>. To install the latest release:</p>
<pre><code class="language-sh">sudo apt install -y lsb-release wget apt-transport-https
sudo wget -O /usr/share/keyrings/matrix-org-archive-keyring.gpg https://packages.matrix.org/debian/matrix-org-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/matrix-org-archive-keyring.gpg] https://packages.matrix.org/debian/ $(lsb_release -cs) main" |
sudo tee /etc/apt/sources.list.d/matrix-org.list
sudo apt update
sudo apt install matrix-synapse-py3
</code></pre>
<p>Packages are also published for release candidates. To enable the prerelease
channel, add <code>prerelease</code> to the <code>sources.list</code> line. For example:</p>
<pre><code class="language-sh">sudo wget -O /usr/share/keyrings/matrix-org-archive-keyring.gpg https://packages.matrix.org/debian/matrix-org-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/matrix-org-archive-keyring.gpg] https://packages.matrix.org/debian/ $(lsb_release -cs) main prerelease" |
sudo tee /etc/apt/sources.list.d/matrix-org.list
sudo apt update
sudo apt install matrix-synapse-py3
</code></pre>
<p>The fingerprint of the repository signing key (as shown by <code>gpg /usr/share/keyrings/matrix-org-archive-keyring.gpg</code>) is
<code>AAF9AE843A7584B5A3E4CD2BCF45A512DE2DA058</code>.</p>
<h5 id="downstream-debian-packages"><a class="header" href="#downstream-debian-packages">Downstream Debian packages</a></h5>
<p>We do not recommend using the packages from the default Debian <code>buster</code>
repository at this time, as they are old and suffer from known security
vulnerabilities. You can install the latest version of Synapse from
<a href="setup/installation.html#matrixorg-packages">our repository</a> or from <code>buster-backports</code>. Please
see the <a href="https://backports.debian.org/Instructions/">Debian documentation</a>
for information on how to use backports.</p>
<p>If you are using Debian <code>sid</code> or testing, Synapse is available in the default
repositories and it should be possible to install it simply with:</p>
<pre><code class="language-sh">sudo apt install matrix-synapse
</code></pre>
<h5 id="downstream-ubuntu-packages"><a class="header" href="#downstream-ubuntu-packages">Downstream Ubuntu packages</a></h5>
<p>We do not recommend using the packages in the default Ubuntu repository
at this time, as they are old and suffer from known security vulnerabilities.
The latest version of Synapse can be installed from <a href="setup/installation.html#matrixorg-packages">our repository</a>.</p>
<h4 id="fedora"><a class="header" href="#fedora">Fedora</a></h4>
<p>Synapse is in the Fedora repositories as <code>matrix-synapse</code>:</p>
<pre><code class="language-sh">sudo dnf install matrix-synapse
</code></pre>
<p>Oleg Girko provides Fedora RPMs at
<a href="https://obs.infoserver.lv/project/monitor/matrix-synapse">https://obs.infoserver.lv/project/monitor/matrix-synapse</a></p>
<h4 id="opensuse-1"><a class="header" href="#opensuse-1">OpenSUSE</a></h4>
<p>Synapse is in the OpenSUSE repositories as <code>matrix-synapse</code>:</p>
<pre><code class="language-sh">sudo zypper install matrix-synapse
</code></pre>
<h4 id="suse-linux-enterprise-server"><a class="header" href="#suse-linux-enterprise-server">SUSE Linux Enterprise Server</a></h4>
<p>Unofficial package are built for SLES 15 in the openSUSE:Backports:SLE-15 repository at
<a href="https://download.opensuse.org/repositories/openSUSE:/Backports:/SLE-15/standard/">https://download.opensuse.org/repositories/openSUSE:/Backports:/SLE-15/standard/</a></p>
<h4 id="archlinux-1"><a class="header" href="#archlinux-1">ArchLinux</a></h4>
<p>The quickest way to get up and running with ArchLinux is probably with the community package
<a href="https://www.archlinux.org/packages/community/any/matrix-synapse/">https://www.archlinux.org/packages/community/any/matrix-synapse/</a>, which should pull in most of
the necessary dependencies.</p>
<p>pip may be outdated (6.0.7-1 and needs to be upgraded to 6.0.8-1 ):</p>
<pre><code class="language-sh">sudo pip install --upgrade pip
</code></pre>
<p>If you encounter an error with lib bcrypt causing an Wrong ELF Class:
ELFCLASS32 (x64 Systems), you may need to reinstall py-bcrypt to correctly
compile it under the right architecture. (This should not be needed if
installing under virtualenv):</p>
<pre><code class="language-sh">sudo pip uninstall py-bcrypt
sudo pip install py-bcrypt
</code></pre>
<h4 id="void-linux"><a class="header" href="#void-linux">Void Linux</a></h4>
<p>Synapse can be found in the void repositories as 'synapse':</p>
<pre><code class="language-sh">xbps-install -Su
xbps-install -S synapse
</code></pre>
<h4 id="freebsd"><a class="header" href="#freebsd">FreeBSD</a></h4>
<p>Synapse can be installed via FreeBSD Ports or Packages contributed by Brendan Molloy from:</p>
<ul>
<li>Ports: <code>cd /usr/ports/net-im/py-matrix-synapse && make install clean</code></li>
<li>Packages: <code>pkg install py37-matrix-synapse</code></li>
</ul>
<h4 id="openbsd-1"><a class="header" href="#openbsd-1">OpenBSD</a></h4>
<p>As of OpenBSD 6.7 Synapse is available as a pre-compiled binary. The filesystem
underlying the homeserver directory (defaults to <code>/var/synapse</code>) has to be
mounted with <code>wxallowed</code> (cf. <code>mount(8)</code>), so creating a separate filesystem
and mounting it to <code>/var/synapse</code> should be taken into consideration.</p>
<p>Installing Synapse:</p>
<pre><code class="language-sh">doas pkg_add synapse
</code></pre>
<h4 id="nixos"><a class="header" href="#nixos">NixOS</a></h4>
<p>Robin Lambertz has packaged Synapse for NixOS at:
<a href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/misc/matrix-synapse.nix">https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/misc/matrix-synapse.nix</a></p>
<h2 id="setting-up-synapse"><a class="header" href="#setting-up-synapse">Setting up Synapse</a></h2>
<p>Once you have installed synapse as above, you will need to configure it.</p>
<h3 id="using-postgresql"><a class="header" href="#using-postgresql">Using PostgreSQL</a></h3>
<p>By default Synapse uses an <a href="https://sqlite.org/">SQLite</a> database and in doing so trades
performance for convenience. Almost all installations should opt to use <a href="https://www.postgresql.org">PostgreSQL</a>
instead. Advantages include:</p>
<ul>
<li>significant performance improvements due to the superior threading and
caching model, smarter query optimiser</li>
<li>allowing the DB to be run on separate hardware</li>
</ul>
<p>For information on how to install and use PostgreSQL in Synapse, please see
<a href="setup/../postgres.html">Using Postgres</a></p>
<p>SQLite is only acceptable for testing purposes. SQLite should not be used in
a production server. Synapse will perform poorly when using
SQLite, especially when participating in large rooms.</p>
<h3 id="tls-certificates"><a class="header" href="#tls-certificates">TLS certificates</a></h3>
<p>The default configuration exposes a single HTTP port on the local
interface: <code>http://localhost:8008</code>. It is suitable for local testing,
but for any practical use, you will need Synapse's APIs to be served
over HTTPS.</p>
<p>The recommended way to do so is to set up a reverse proxy on port
<code>8448</code>. You can find documentation on doing so in
<a href="setup/../reverse_proxy.html">the reverse proxy documentation</a>.</p>
<p>Alternatively, you can configure Synapse to expose an HTTPS port. To do
so, you will need to edit <code>homeserver.yaml</code>, as follows:</p>
<ul>
<li>First, under the <code>listeners</code> section, uncomment the configuration for the
TLS-enabled listener. (Remove the hash sign (<code>#</code>) at the start of
each line). The relevant lines are like this:</li>
</ul>
<pre><code class="language-yaml"> - port: 8448
type: http
tls: true
resources:
- names: [client, federation]
</code></pre>
<ul>
<li>
<p>You will also need to uncomment the <code>tls_certificate_path</code> and
<code>tls_private_key_path</code> lines under the <code>TLS</code> section. You will need to manage
provisioning of these certificates yourself.</p>
<p>If you are using your own certificate, be sure to use a <code>.pem</code> file that
includes the full certificate chain including any intermediate certificates
(for instance, if using certbot, use <code>fullchain.pem</code> as your certificate, not
<code>cert.pem</code>).</p>
</li>
</ul>
<p>For a more detailed guide to configuring your server for federation, see
<a href="setup/../federate.html">Federation</a>.</p>
<h3 id="client-well-known-uri"><a class="header" href="#client-well-known-uri">Client Well-Known URI</a></h3>
<p>Setting up the client Well-Known URI is optional but if you set it up, it will
allow users to enter their full username (e.g. <code>@user:<server_name></code>) into clients
which support well-known lookup to automatically configure the homeserver and
identity server URLs. This is useful so that users don't have to memorize or think
about the actual homeserver URL you are using.</p>
<p>The URL <code>https://<server_name>/.well-known/matrix/client</code> should return JSON in
the following format.</p>
<pre><code class="language-json">{
"m.homeserver": {
"base_url": "https://<matrix.example.com>"
}
}
</code></pre>
<p>It can optionally contain identity server information as well.</p>
<pre><code class="language-json">{
"m.homeserver": {
"base_url": "https://<matrix.example.com>"
},
"m.identity_server": {
"base_url": "https://<identity.example.com>"
}
}
</code></pre>
<p>To work in browser based clients, the file must be served with the appropriate
Cross-Origin Resource Sharing (CORS) headers. A recommended value would be
<code>Access-Control-Allow-Origin: *</code> which would allow all browser based clients to
view it.</p>
<p>In nginx this would be something like:</p>
<pre><code class="language-nginx">location /.well-known/matrix/client {
return 200 '{"m.homeserver": {"base_url": "https://<matrix.example.com>"}}';
default_type application/json;
add_header Access-Control-Allow-Origin *;
}
</code></pre>
<p>You should also ensure the <code>public_baseurl</code> option in <code>homeserver.yaml</code> is set
correctly. <code>public_baseurl</code> should be set to the URL that clients will use to
connect to your server. This is the same URL you put for the <code>m.homeserver</code>
<code>base_url</code> above.</p>
<pre><code class="language-yaml">public_baseurl: "https://<matrix.example.com>"
</code></pre>
<h3 id="email"><a class="header" href="#email">Email</a></h3>
<p>It is desirable for Synapse to have the capability to send email. This allows
Synapse to send password reset emails, send verifications when an email address
is added to a user's account, and send email notifications to users when they
receive new messages.</p>
<p>To configure an SMTP server for Synapse, modify the configuration section
headed <code>email</code>, and be sure to have at least the <code>smtp_host</code>, <code>smtp_port</code>
and <code>notif_from</code> fields filled out. You may also need to set <code>smtp_user</code>,
<code>smtp_pass</code>, and <code>require_transport_security</code>.</p>
<p>If email is not configured, password reset, registration and notifications via
email will be disabled.</p>
<h3 id="registering-a-user"><a class="header" href="#registering-a-user">Registering a user</a></h3>
<p>The easiest way to create a new user is to do so from a client like <a href="https://element.io/">Element</a>.</p>
<p>Alternatively, you can do so from the command line. This can be done as follows:</p>
<ol>
<li>If synapse was installed via pip, activate the virtualenv as follows (if Synapse was
installed via a prebuilt package, <code>register_new_matrix_user</code> should already be
on the search path):
<pre><code class="language-sh">cd ~/synapse
source env/bin/activate
synctl start # if not already running
</code></pre>
</li>
<li>Run the following command:
<pre><code class="language-sh">register_new_matrix_user -c homeserver.yaml http://localhost:8008
</code></pre>
</li>
</ol>
<p>This will prompt you to add details for the new user, and will then connect to
the running Synapse to create the new user. For example:</p>
<pre><code>New user localpart: erikj
Password:
Confirm password:
Make admin [no]:
Success!
</code></pre>
<p>This process uses a setting <code>registration_shared_secret</code> in
<code>homeserver.yaml</code>, which is shared between Synapse itself and the
<code>register_new_matrix_user</code> script. It doesn't matter what it is (a random
value is generated by <code>--generate-config</code>), but it should be kept secret, as
anyone with knowledge of it can register users, including admin accounts,
on your server even if <code>enable_registration</code> is <code>false</code>.</p>
<h3 id="setting-up-a-turn-server"><a class="header" href="#setting-up-a-turn-server">Setting up a TURN server</a></h3>
<p>For reliable VoIP calls to be routed via this homeserver, you MUST configure
a TURN server. See <a href="setup/../turn-howto.html">TURN setup</a> for details.</p>
<h3 id="url-previews"><a class="header" href="#url-previews">URL previews</a></h3>
<p>Synapse includes support for previewing URLs, which is disabled by default. To
turn it on you must enable the <code>url_preview_enabled: True</code> config parameter
and explicitly specify the IP ranges that Synapse is not allowed to spider for
previewing in the <code>url_preview_ip_range_blacklist</code> configuration parameter.
This is critical from a security perspective to stop arbitrary Matrix users
spidering 'internal' URLs on your network. At the very least we recommend that
your loopback and RFC1918 IP addresses are blacklisted.</p>
<p>This also requires the optional <code>lxml</code> python dependency to be installed. This
in turn requires the <code>libxml2</code> library to be available - on Debian/Ubuntu this
means <code>apt-get install libxml2-dev</code>, or equivalent for your OS.</p>
<h3 id="troubleshooting-installation"><a class="header" href="#troubleshooting-installation">Troubleshooting Installation</a></h3>
<p><code>pip</code> seems to leak <em>lots</em> of memory during installation. For instance, a Linux
host with 512MB of RAM may run out of memory whilst installing Twisted. If this
happens, you will have to individually install the dependencies which are
failing, e.g.:</p>
<pre><code class="language-sh">pip install twisted
</code></pre>
<p>If you have any other problems, feel free to ask in
<a href="https://matrix.to/#/#synapse:matrix.org">#synapse:matrix.org</a>.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="using-postgres"><a class="header" href="#using-postgres">Using Postgres</a></h1>
<p>Synapse supports PostgreSQL versions 9.6 or later.</p>
<h2 id="install-postgres-client-libraries"><a class="header" href="#install-postgres-client-libraries">Install postgres client libraries</a></h2>
<p>Synapse will require the python postgres client library in order to
connect to a postgres database.</p>
<ul>
<li>
<p>If you are using the <a href="setup/installation.html#matrixorg-packages">matrix.org debian/ubuntu
packages</a>, the necessary python
library will already be installed, but you will need to ensure the
low-level postgres library is installed, which you can do with
<code>apt install libpq5</code>.</p>
</li>
<li>
<p>For other pre-built packages, please consult the documentation from
the relevant package.</p>
</li>
<li>
<p>If you installed synapse <a href="setup/installation.html#installing-from-source">in a
virtualenv</a>, you can install
the library with:</p>
<pre><code>~/synapse/env/bin/pip install "matrix-synapse[postgres]"
</code></pre>
<p>(substituting the path to your virtualenv for <code>~/synapse/env</code>, if
you used a different path). You will require the postgres
development files. These are in the <code>libpq-dev</code> package on
Debian-derived distributions.</p>
</li>
</ul>
<h2 id="set-up-database"><a class="header" href="#set-up-database">Set up database</a></h2>
<p>Assuming your PostgreSQL database user is called <code>postgres</code>, first authenticate as the database user with:</p>
<pre><code>su - postgres
# Or, if your system uses sudo to get administrative rights
sudo -u postgres bash
</code></pre>
<p>Then, create a postgres user and a database with:</p>
<pre><code># this will prompt for a password for the new user
createuser --pwprompt synapse_user
createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse_user synapse
</code></pre>
<p>The above will create a user called <code>synapse_user</code>, and a database called
<code>synapse</code>.</p>
<p>Note that the PostgreSQL database <em>must</em> have the correct encoding set
(as shown above), otherwise it will not be able to store UTF8 strings.</p>
<p>You may need to enable password authentication so <code>synapse_user</code> can
connect to the database. See
<a href="https://www.postgresql.org/docs/current/auth-pg-hba-conf.html">https://www.postgresql.org/docs/current/auth-pg-hba-conf.html</a>.</p>
<h2 id="synapse-config"><a class="header" href="#synapse-config">Synapse config</a></h2>
<p>When you are ready to start using PostgreSQL, edit the <code>database</code>
section in your config file to match the following lines:</p>
<pre><code class="language-yaml">database:
name: psycopg2
args:
user: <user>
password: <pass>
database: <db>
host: <host>
cp_min: 5
cp_max: 10
</code></pre>
<p>All key, values in <code>args</code> are passed to the <code>psycopg2.connect(..)</code>
function, except keys beginning with <code>cp_</code>, which are consumed by the
twisted adbapi connection pool. See the <a href="https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS">libpq
documentation</a>
for a list of options which can be passed.</p>
<p>You should consider tuning the <code>args.keepalives_*</code> options if there is any danger of
the connection between your homeserver and database dropping, otherwise Synapse
may block for an extended period while it waits for a response from the
database server. Example values might be:</p>
<pre><code class="language-yaml">database:
args:
# ... as above
# seconds of inactivity after which TCP should send a keepalive message to the server
keepalives_idle: 10
# the number of seconds after which a TCP keepalive message that is not
# acknowledged by the server should be retransmitted
keepalives_interval: 10
# the number of TCP keepalives that can be lost before the client's connection
# to the server is considered dead
keepalives_count: 3
</code></pre>
<h2 id="tuning-postgres"><a class="header" href="#tuning-postgres">Tuning Postgres</a></h2>
<p>The default settings should be fine for most deployments. For larger
scale deployments tuning some of the settings is recommended, details of
which can be found at
<a href="https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server">https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server</a>.</p>
<p>In particular, we've found tuning the following values helpful for
performance:</p>
<ul>
<li><code>shared_buffers</code></li>
<li><code>effective_cache_size</code></li>
<li><code>work_mem</code></li>
<li><code>maintenance_work_mem</code></li>
<li><code>autovacuum_work_mem</code></li>
</ul>
<p>Note that the appropriate values for those fields depend on the amount
of free memory the database host has available.</p>
<h2 id="porting-from-sqlite"><a class="header" href="#porting-from-sqlite">Porting from SQLite</a></h2>
<h3 id="overview"><a class="header" href="#overview">Overview</a></h3>
<p>The script <code>synapse_port_db</code> allows porting an existing synapse server
backed by SQLite to using PostgreSQL. This is done in as a two phase
process:</p>
<ol>
<li>Copy the existing SQLite database to a separate location and run
the port script against that offline database.</li>
<li>Shut down the server. Rerun the port script to port any data that
has come in since taking the first snapshot. Restart server against
the PostgreSQL database.</li>
</ol>
<p>The port script is designed to be run repeatedly against newer snapshots
of the SQLite database file. This makes it safe to repeat step 1 if
there was a delay between taking the previous snapshot and being ready
to do step 2.</p>
<p>It is safe to at any time kill the port script and restart it.</p>
<p>Note that the database may take up significantly more (25% - 100% more)
space on disk after porting to Postgres.</p>
<h3 id="using-the-port-script"><a class="header" href="#using-the-port-script">Using the port script</a></h3>
<p>Firstly, shut down the currently running synapse server and copy its
database file (typically <code>homeserver.db</code>) to another location. Once the
copy is complete, restart synapse. For instance:</p>
<pre><code>./synctl stop
cp homeserver.db homeserver.db.snapshot
./synctl start
</code></pre>
<p>Copy the old config file into a new config file:</p>
<pre><code>cp homeserver.yaml homeserver-postgres.yaml
</code></pre>
<p>Edit the database section as described in the section <em>Synapse config</em>
above and with the SQLite snapshot located at <code>homeserver.db.snapshot</code>
simply run:</p>
<pre><code>synapse_port_db --sqlite-database homeserver.db.snapshot \
--postgres-config homeserver-postgres.yaml
</code></pre>
<p>The flag <code>--curses</code> displays a coloured curses progress UI.</p>
<p>If the script took a long time to complete, or time has otherwise passed
since the original snapshot was taken, repeat the previous steps with a
newer snapshot.</p>
<p>To complete the conversion shut down the synapse server and run the port
script one last time, e.g. if the SQLite database is at <code>homeserver.db</code>
run:</p>
<pre><code>synapse_port_db --sqlite-database homeserver.db \
--postgres-config homeserver-postgres.yaml
</code></pre>
<p>Once that has completed, change the synapse config to point at the
PostgreSQL database configuration file <code>homeserver-postgres.yaml</code>:</p>
<pre><code>./synctl stop
mv homeserver.yaml homeserver-old-sqlite.yaml
mv homeserver-postgres.yaml homeserver.yaml
./synctl start
</code></pre>
<p>Synapse should now be running against PostgreSQL.</p>
<h2 id="troubleshooting"><a class="header" href="#troubleshooting">Troubleshooting</a></h2>
<h3 id="alternative-auth-methods"><a class="header" href="#alternative-auth-methods">Alternative auth methods</a></h3>
<p>If you get an error along the lines of <code>FATAL: Ident authentication failed for user "synapse_user"</code>, you may need to use an authentication method other than
<code>ident</code>:</p>
<ul>
<li>
<p>If the <code>synapse_user</code> user has a password, add the password to the <code>database:</code>
section of <code>homeserver.yaml</code>. Then add the following to <code>pg_hba.conf</code>:</p>
<pre><code>host synapse synapse_user ::1/128 md5 # or `scram-sha-256` instead of `md5` if you use that
</code></pre>
</li>
<li>
<p>If the <code>synapse_user</code> user does not have a password, then a password doesn't
have to be added to <code>homeserver.yaml</code>. But the following does need to be added
to <code>pg_hba.conf</code>:</p>
<pre><code>host synapse synapse_user ::1/128 trust
</code></pre>
</li>
</ul>
<p>Note that line order matters in <code>pg_hba.conf</code>, so make sure that if you do add a
new line, it is inserted before:</p>
<pre><code>host all all ::1/128 ident
</code></pre>
<h3 id="fixing-incorrect-collate-or-ctype"><a class="header" href="#fixing-incorrect-collate-or-ctype">Fixing incorrect <code>COLLATE</code> or <code>CTYPE</code></a></h3>
<p>Synapse will refuse to set up a new database if it has the wrong values of
<code>COLLATE</code> and <code>CTYPE</code> set, and will log warnings on existing databases. Using
different locales can cause issues if the locale library is updated from
underneath the database, or if a different version of the locale is used on any
replicas.</p>
<p>The safest way to fix the issue is to dump the database and recreate it with
the correct locale parameter (as shown above). It is also possible to change the
parameters on a live database and run a <code>REINDEX</code> on the entire database,
however extreme care must be taken to avoid database corruption.</p>
<p>Note that the above may fail with an error about duplicate rows if corruption
has already occurred, and such duplicate rows will need to be manually removed.</p>
<h3 id="fixing-inconsistent-sequences-error"><a class="header" href="#fixing-inconsistent-sequences-error">Fixing inconsistent sequences error</a></h3>
<p>Synapse uses Postgres sequences to generate IDs for various tables. A sequence
and associated table can get out of sync if, for example, Synapse has been
downgraded and then upgraded again.</p>
<p>To fix the issue shut down Synapse (including any and all workers) and run the
SQL command included in the error message. Once done Synapse should start
successfully.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="using-a-reverse-proxy-with-synapse"><a class="header" href="#using-a-reverse-proxy-with-synapse">Using a reverse proxy with Synapse</a></h1>
<p>It is recommended to put a reverse proxy such as
<a href="https://nginx.org/en/docs/http/ngx_http_proxy_module.html">nginx</a>,
<a href="https://httpd.apache.org/docs/current/mod/mod_proxy_http.html">Apache</a>,
<a href="https://caddyserver.com/docs/quick-starts/reverse-proxy">Caddy</a>,
<a href="https://www.haproxy.org/">HAProxy</a> or
<a href="https://man.openbsd.org/relayd.8">relayd</a> in front of Synapse. One advantage
of doing so is that it means that you can expose the default https port
(443) to Matrix clients without needing to run Synapse with root
privileges.</p>
<p>You should configure your reverse proxy to forward requests to <code>/_matrix</code> or
<code>/_synapse/client</code> to Synapse, and have it set the <code>X-Forwarded-For</code> and
<code>X-Forwarded-Proto</code> request headers.</p>
<p>You should remember that Matrix clients and other Matrix servers do not
necessarily need to connect to your server via the same server name or
port. Indeed, clients will use port 443 by default, whereas servers default to
port 8448. Where these are different, we refer to the 'client port' and the
'federation port'. See <a href="https://matrix.org/docs/spec/server_server/latest#resolving-server-names">the Matrix
specification</a>
for more details of the algorithm used for federation connections, and
<a href="delegate.html">Delegation</a> for instructions on setting up delegation.</p>
<p><strong>NOTE</strong>: Your reverse proxy must not <code>canonicalise</code> or <code>normalise</code>
the requested URI in any way (for example, by decoding <code>%xx</code> escapes).
Beware that Apache <em>will</em> canonicalise URIs unless you specify
<code>nocanon</code>.</p>
<p>Let's assume that we expect clients to connect to our server at
<code>https://matrix.example.com</code>, and other servers to connect at
<code>https://example.com:8448</code>. The following sections detail the configuration of
the reverse proxy and the homeserver.</p>
<h2 id="reverse-proxy-configuration-examples"><a class="header" href="#reverse-proxy-configuration-examples">Reverse-proxy configuration examples</a></h2>
<p><strong>NOTE</strong>: You only need one of these.</p>
<h3 id="nginx"><a class="header" href="#nginx">nginx</a></h3>
<pre><code>server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
# For the federation port
listen 8448 ssl http2 default_server;
listen [::]:8448 ssl http2 default_server;
server_name matrix.example.com;
location ~* ^(\/_matrix|\/_synapse\/client) {
proxy_pass http://localhost:8008;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
# Nginx by default only allows file uploads up to 1M in size
# Increase client_max_body_size to match max_upload_size defined in homeserver.yaml
client_max_body_size 50M;
}
}
</code></pre>
<p><strong>NOTE</strong>: Do not add a path after the port in <code>proxy_pass</code>, otherwise nginx will
canonicalise/normalise the URI.</p>
<h3 id="caddy-1"><a class="header" href="#caddy-1">Caddy 1</a></h3>
<pre><code>matrix.example.com {
proxy /_matrix http://localhost:8008 {
transparent
}
proxy /_synapse/client http://localhost:8008 {
transparent
}
}
example.com:8448 {
proxy / http://localhost:8008 {
transparent
}
}
</code></pre>
<h3 id="caddy-2"><a class="header" href="#caddy-2">Caddy 2</a></h3>
<pre><code>matrix.example.com {
reverse_proxy /_matrix/* http://localhost:8008
reverse_proxy /_synapse/client/* http://localhost:8008
}
example.com:8448 {
reverse_proxy http://localhost:8008
}
</code></pre>
<p><a href="delegate.html">Delegation</a> example:</p>
<pre><code>(matrix-well-known-header) {
# Headers
header Access-Control-Allow-Origin "*"
header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"
header Content-Type "application/json"
}
example.com {
handle /.well-known/matrix/server {
import matrix-well-known-header
respond `{"m.server":"matrix.example.com:443"}`
}
handle /.well-known/matrix/client {
import matrix-well-known-header
respond `{"m.homeserver":{"base_url":"https://matrix.example.com"},"m.identity_server":{"base_url":"https://identity.example.com"}}`
}
}
matrix.example.com {
reverse_proxy /_matrix/* http://localhost:8008
reverse_proxy /_synapse/client/* http://localhost:8008
}
</code></pre>
<h3 id="apache"><a class="header" href="#apache">Apache</a></h3>
<pre><code><VirtualHost *:443>
SSLEngine on
ServerName matrix.example.com
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
AllowEncodedSlashes NoDecode
ProxyPreserveHost on
ProxyPass /_matrix http://127.0.0.1:8008/_matrix nocanon
ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix
ProxyPass /_synapse/client http://127.0.0.1:8008/_synapse/client nocanon
ProxyPassReverse /_synapse/client http://127.0.0.1:8008/_synapse/client
</VirtualHost>
<VirtualHost *:8448>
SSLEngine on
ServerName example.com
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
AllowEncodedSlashes NoDecode
ProxyPass /_matrix http://127.0.0.1:8008/_matrix nocanon
ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix
</VirtualHost>
</code></pre>
<p><strong>NOTE</strong>: ensure the <code>nocanon</code> options are included.</p>
<p><strong>NOTE 2</strong>: It appears that Synapse is currently incompatible with the ModSecurity module for Apache (<code>mod_security2</code>). If you need it enabled for other services on your web server, you can disable it for Synapse's two VirtualHosts by including the following lines before each of the two <code></VirtualHost></code> above:</p>
<pre><code><IfModule security2_module>
SecRuleEngine off
</IfModule>
</code></pre>
<p><strong>NOTE 3</strong>: Missing <code>ProxyPreserveHost on</code> can lead to a redirect loop.</p>
<h3 id="haproxy"><a class="header" href="#haproxy">HAProxy</a></h3>
<pre><code>frontend https
bind :::443 v4v6 ssl crt /etc/ssl/haproxy/ strict-sni alpn h2,http/1.1
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
http-request set-header X-Forwarded-For %[src]
# Matrix client traffic
acl matrix-host hdr(host) -i matrix.example.com
acl matrix-path path_beg /_matrix
acl matrix-path path_beg /_synapse/client
use_backend matrix if matrix-host matrix-path
frontend matrix-federation
bind :::8448 v4v6 ssl crt /etc/ssl/haproxy/synapse.pem alpn h2,http/1.1
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
http-request set-header X-Forwarded-For %[src]
default_backend matrix
backend matrix
server matrix 127.0.0.1:8008
</code></pre>
<h3 id="relayd"><a class="header" href="#relayd">Relayd</a></h3>
<pre><code>table <webserver> { 127.0.0.1 }
table <matrixserver> { 127.0.0.1 }
http protocol "https" {
tls { no tlsv1.0, ciphers "HIGH" }
tls keypair "example.com"
match header set "X-Forwarded-For" value "$REMOTE_ADDR"
match header set "X-Forwarded-Proto" value "https"
# set CORS header for .well-known/matrix/server, .well-known/matrix/client
# httpd does not support setting headers, so do it here
match request path "/.well-known/matrix/*" tag "matrix-cors"
match response tagged "matrix-cors" header set "Access-Control-Allow-Origin" value "*"
pass quick path "/_matrix/*" forward to <matrixserver>
pass quick path "/_synapse/client/*" forward to <matrixserver>
# pass on non-matrix traffic to webserver
pass forward to <webserver>
}
relay "https_traffic" {
listen on egress port 443 tls
protocol "https"
forward to <matrixserver> port 8008 check tcp
forward to <webserver> port 8080 check tcp
}
http protocol "matrix" {
tls { no tlsv1.0, ciphers "HIGH" }
tls keypair "example.com"
block
pass quick path "/_matrix/*" forward to <matrixserver>
pass quick path "/_synapse/client/*" forward to <matrixserver>
}
relay "matrix_federation" {
listen on egress port 8448 tls
protocol "matrix"
forward to <matrixserver> port 8008 check tcp
}
</code></pre>
<h2 id="homeserver-configuration"><a class="header" href="#homeserver-configuration">Homeserver Configuration</a></h2>
<p>You will also want to set <code>bind_addresses: ['127.0.0.1']</code> and
<code>x_forwarded: true</code> for port 8008 in <code>homeserver.yaml</code> to ensure that
client IP addresses are recorded correctly.</p>
<p>Having done so, you can then use <code>https://matrix.example.com</code> (instead
of <code>https://matrix.example.com:8448</code>) as the "Custom server" when
connecting to Synapse from a client.</p>
<h2 id="health-check-endpoint"><a class="header" href="#health-check-endpoint">Health check endpoint</a></h2>
<p>Synapse exposes a health check endpoint for use by reverse proxies.
Each configured HTTP listener has a <code>/health</code> endpoint which always returns
200 OK (and doesn't get logged).</p>
<h2 id="synapse-administration-endpoints"><a class="header" href="#synapse-administration-endpoints">Synapse administration endpoints</a></h2>
<p>Endpoints for administering your Synapse instance are placed under
<code>/_synapse/admin</code>. These require authentication through an access token of an
admin user. However as access to these endpoints grants the caller a lot of power,
we do not recommend exposing them to the public internet without good reason.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="overview-1"><a class="header" href="#overview-1">Overview</a></h1>
<p>This document explains how to enable VoIP relaying on your Home Server with
TURN.</p>
<p>The synapse Matrix Home Server supports integration with TURN server via the
<a href="https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00">TURN server REST API</a>. This
allows the Home Server to generate credentials that are valid for use on the
TURN server through the use of a secret shared between the Home Server and the
TURN server.</p>
<p>The following sections describe how to install <a href="https://github.com/coturn/coturn">coturn</a> (which implements the TURN REST API) and integrate it with synapse.</p>
<h2 id="requirements"><a class="header" href="#requirements">Requirements</a></h2>
<p>For TURN relaying with <code>coturn</code> to work, it must be hosted on a server/endpoint with a public IP.</p>
<p>Hosting TURN behind a NAT (even with appropriate port forwarding) is known to cause issues
and to often not work.</p>
<h2 id="coturn-setup"><a class="header" href="#coturn-setup"><code>coturn</code> setup</a></h2>
<h3 id="initial-installation"><a class="header" href="#initial-installation">Initial installation</a></h3>
<p>The TURN daemon <code>coturn</code> is available from a variety of sources such as native package managers, or installation from source.</p>
<h4 id="debian-installation"><a class="header" href="#debian-installation">Debian installation</a></h4>
<p>Just install the debian package:</p>
<pre><code class="language-sh">apt install coturn
</code></pre>
<p>This will install and start a systemd service called <code>coturn</code>.</p>
<h4 id="source-installation"><a class="header" href="#source-installation">Source installation</a></h4>
<ol>
<li>
<p>Download the <a href="https://github.com/coturn/coturn/releases/latest">latest release</a> from github. Unpack it and <code>cd</code> into the directory.</p>
</li>
<li>
<p>Configure it:</p>
<pre><code>./configure
</code></pre>
<p>You may need to install <code>libevent2</code>: if so, you should do so in
the way recommended by your operating system. You can ignore
warnings about lack of database support: a database is unnecessary
for this purpose.</p>
</li>
<li>
<p>Build and install it:</p>
<pre><code>make
make install
</code></pre>
</li>
</ol>
<h3 id="configuration"><a class="header" href="#configuration">Configuration</a></h3>
<ol>
<li>
<p>Create or edit the config file in <code>/etc/turnserver.conf</code>. The relevant
lines, with example values, are:</p>
<pre><code>use-auth-secret
static-auth-secret=[your secret key here]
realm=turn.myserver.org
</code></pre>
<p>See <code>turnserver.conf</code> for explanations of the options. One way to generate
the <code>static-auth-secret</code> is with <code>pwgen</code>:</p>
<pre><code>pwgen -s 64 1
</code></pre>
<p>A <code>realm</code> must be specified, but its value is somewhat arbitrary. (It is
sent to clients as part of the authentication flow.) It is conventional to
set it to be your server name.</p>
</li>
<li>
<p>You will most likely want to configure coturn to write logs somewhere. The
easiest way is normally to send them to the syslog:</p>
<pre><code>syslog
</code></pre>
<p>(in which case, the logs will be available via <code>journalctl -u coturn</code> on a
systemd system). Alternatively, coturn can be configured to write to a
logfile - check the example config file supplied with coturn.</p>
</li>
<li>
<p>Consider your security settings. TURN lets users request a relay which will
connect to arbitrary IP addresses and ports. The following configuration is
suggested as a minimum starting point:</p>
<pre><code># VoIP traffic is all UDP. There is no reason to let users connect to arbitrary TCP endpoints via the relay.
no-tcp-relay
# don't let the relay ever try to connect to private IP address ranges within your network (if any)
# given the turn server is likely behind your firewall, remember to include any privileged public IPs too.
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
# special case the turn server itself so that client->TURN->TURN->client flows work
allowed-peer-ip=10.0.0.1
# consider whether you want to limit the quota of relayed streams per user (or total) to avoid risk of DoS.
user-quota=12 # 4 streams per video call, so 12 streams = 3 simultaneous relayed calls per user.
total-quota=1200
</code></pre>
</li>
<li>
<p>Also consider supporting TLS/DTLS. To do this, add the following settings
to <code>turnserver.conf</code>:</p>
<pre><code># TLS certificates, including intermediate certs.
# For Let's Encrypt certificates, use `fullchain.pem` here.
cert=/path/to/fullchain.pem
# TLS private key file
pkey=/path/to/privkey.pem
</code></pre>
<p>In this case, replace the <code>turn:</code> schemes in the <code>turn_uri</code> settings below
with <code>turns:</code>.</p>
<p>We recommend that you only try to set up TLS/DTLS once you have set up a
basic installation and got it working.</p>
</li>
<li>
<p>Ensure your firewall allows traffic into the TURN server on the ports
you've configured it to listen on (By default: 3478 and 5349 for TURN
traffic (remember to allow both TCP and UDP traffic), and ports 49152-65535
for the UDP relay.)</p>
</li>
<li>
<p>We do not recommend running a TURN server behind NAT, and are not aware of
anyone doing so successfully.</p>
<p>If you want to try it anyway, you will at least need to tell coturn its
external IP address:</p>
<pre><code>external-ip=192.88.99.1
</code></pre>
<p>... and your NAT gateway must forward all of the relayed ports directly
(eg, port 56789 on the external IP must be always be forwarded to port
56789 on the internal IP).</p>
<p>If you get this working, let us know!</p>
</li>
<li>
<p>(Re)start the turn server:</p>
<ul>
<li>
<p>If you used the Debian package (or have set up a systemd unit yourself):</p>
<pre><code class="language-sh">systemctl restart coturn
</code></pre>
</li>
<li>
<p>If you installed from source:</p>
<pre><code class="language-sh">bin/turnserver -o
</code></pre>
</li>
</ul>
</li>
</ol>
<h2 id="synapse-setup"><a class="header" href="#synapse-setup">Synapse setup</a></h2>
<p>Your home server configuration file needs the following extra keys:</p>
<ol>
<li>"<code>turn_uris</code>": This needs to be a yaml list of public-facing URIs
for your TURN server to be given out to your clients. Add separate
entries for each transport your TURN server supports.</li>
<li>"<code>turn_shared_secret</code>": This is the secret shared between your
Home server and your TURN server, so you should set it to the same
string you used in turnserver.conf.</li>
<li>"<code>turn_user_lifetime</code>": This is the amount of time credentials
generated by your Home Server are valid for (in milliseconds).
Shorter times offer less potential for abuse at the expense of
increased traffic between web clients and your home server to
refresh credentials. The TURN REST API specification recommends
one day (86400000).</li>
<li>"<code>turn_allow_guests</code>": Whether to allow guest users to use the
TURN server. This is enabled by default, as otherwise VoIP will
not work reliably for guests. However, it does introduce a
security risk as it lets guests connect to arbitrary endpoints
without having gone through a CAPTCHA or similar to register a
real account.</li>
</ol>
<p>As an example, here is the relevant section of the config file for <code>matrix.org</code>. The
<code>turn_uris</code> are appropriate for TURN servers listening on the default ports, with no TLS.</p>
<pre><code>turn_uris: [ "turn:turn.matrix.org?transport=udp", "turn:turn.matrix.org?transport=tcp" ]
turn_shared_secret: "n0t4ctuAllymatr1Xd0TorgSshar3d5ecret4obvIousreAsons"
turn_user_lifetime: 86400000
turn_allow_guests: True
</code></pre>
<p>After updating the homeserver configuration, you must restart synapse:</p>
<ul>
<li>If you use synctl:
<pre><code class="language-sh">cd /where/you/run/synapse
./synctl restart
</code></pre>
</li>
<li>If you use systemd:
<pre><code>systemctl restart matrix-synapse.service
</code></pre>
</li>
</ul>
<p>... and then reload any clients (or wait an hour for them to refresh their
settings).</p>
<h2 id="troubleshooting-1"><a class="header" href="#troubleshooting-1">Troubleshooting</a></h2>
<p>The normal symptoms of a misconfigured TURN server are that calls between
devices on different networks ring, but get stuck at "call
connecting". Unfortunately, troubleshooting this can be tricky.</p>
<p>Here are a few things to try:</p>
<ul>
<li>
<p>Check that your TURN server is not behind NAT. As above, we're not aware of
anyone who has successfully set this up.</p>
</li>
<li>
<p>Check that you have opened your firewall to allow TCP and UDP traffic to the
TURN ports (normally 3478 and 5479).</p>
</li>
<li>
<p>Check that you have opened your firewall to allow UDP traffic to the UDP
relay ports (49152-65535 by default).</p>
</li>
<li>
<p>Some WebRTC implementations (notably, that of Google Chrome) appear to get
confused by TURN servers which are reachable over IPv6 (this appears to be
an unexpected side-effect of its handling of multiple IP addresses as
defined by
<a href="https://tools.ietf.org/html/draft-ietf-rtcweb-ip-handling-12"><code>draft-ietf-rtcweb-ip-handling</code></a>).</p>
<p>Try removing any AAAA records for your TURN server, so that it is only
reachable over IPv4.</p>
</li>
<li>
<p>Enable more verbose logging in coturn via the <code>verbose</code> setting:</p>
<pre><code>verbose
</code></pre>
<p>... and then see if there are any clues in its logs.</p>
</li>
<li>
<p>If you are using a browser-based client under Chrome, check
<code>chrome://webrtc-internals/</code> for insights into the internals of the
negotiation. On Firefox, check the "Connection Log" on <code>about:webrtc</code>.</p>
<p>(Understanding the output is beyond the scope of this document!)</p>
</li>
<li>
<p>You can test your Matrix homeserver TURN setup with https://test.voip.librepush.net/.
Note that this test is not fully reliable yet, so don't be discouraged if
the test fails.
<a href="https://github.com/matrix-org/voip-tester">Here</a> is the github repo of the
source of the tester, where you can file bug reports.</p>
</li>
<li>
<p>There is a WebRTC test tool at
https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/. To
use it, you will need a username/password for your TURN server. You can
either:</p>
<ul>
<li>
<p>look for the <code>GET /_matrix/client/r0/voip/turnServer</code> request made by a
matrix client to your homeserver in your browser's network inspector. In
the response you should see <code>username</code> and <code>password</code>. Or:</p>
</li>
<li>
<p>Use the following shell commands:</p>
<pre><code class="language-sh">secret=staticAuthSecretHere
u=$((`date +%s` + 3600)):test
p=$(echo -n $u | openssl dgst -hmac $secret -sha1 -binary | base64)
echo -e "username: $u\npassword: $p"
</code></pre>
<p>Or:</p>
</li>
<li>
<p>Temporarily configure coturn to accept a static username/password. To do
this, comment out <code>use-auth-secret</code> and <code>static-auth-secret</code> and add the
following:</p>
<pre><code>lt-cred-mech
user=username:password
</code></pre>
<p><strong>Note</strong>: these settings will not take effect unless <code>use-auth-secret</code>
and <code>static-auth-secret</code> are disabled.</p>
<p>Restart coturn after changing the configuration file.</p>
<p>Remember to restore the original settings to go back to testing with
Matrix clients!</p>
</li>
</ul>
<p>If the TURN server is working correctly, you should see at least one <code>relay</code>
entry in the results.</p>
</li>
</ul>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="delegation"><a class="header" href="#delegation">Delegation</a></h1>
<p>By default, other homeservers will expect to be able to reach yours via
your <code>server_name</code>, on port 8448. For example, if you set your <code>server_name</code>
to <code>example.com</code> (so that your user names look like <code>@user:example.com</code>),
other servers will try to connect to yours at <code>https://example.com:8448/</code>.</p>
<p>Delegation is a Matrix feature allowing a homeserver admin to retain a
<code>server_name</code> of <code>example.com</code> so that user IDs, room aliases, etc continue
to look like <code>*:example.com</code>, whilst having federation traffic routed
to a different server and/or port (e.g. <code>synapse.example.com:443</code>).</p>
<h2 id="well-known-delegation"><a class="header" href="#well-known-delegation">.well-known delegation</a></h2>
<p>To use this method, you need to be able to alter the
<code>server_name</code> 's https server to serve the <code>/.well-known/matrix/server</code>
URL. Having an active server (with a valid TLS certificate) serving your
<code>server_name</code> domain is out of the scope of this documentation.</p>
<p>The URL <code>https://<server_name>/.well-known/matrix/server</code> should
return a JSON structure containing the key <code>m.server</code> like so:</p>
<pre><code class="language-json">{
"m.server": "<synapse.server.name>[:<yourport>]"
}
</code></pre>
<p>In our example, this would mean that URL <code>https://example.com/.well-known/matrix/server</code>
should return:</p>
<pre><code class="language-json">{
"m.server": "synapse.example.com:443"
}
</code></pre>
<p>Note, specifying a port is optional. If no port is specified, then it defaults
to 8448.</p>
<p>With .well-known delegation, federating servers will check for a valid TLS
certificate for the delegated hostname (in our example: <code>synapse.example.com</code>).</p>
<h2 id="srv-dns-record-delegation"><a class="header" href="#srv-dns-record-delegation">SRV DNS record delegation</a></h2>
<p>It is also possible to do delegation using a SRV DNS record. However, that is
considered an advanced topic since it's a bit complex to set up, and <code>.well-known</code>
delegation is already enough in most cases.</p>
<p>However, if you really need it, you can find some documentation on how such a
record should look like and how Synapse will use it in <a href="https://matrix.org/docs/spec/server_server/latest#resolving-server-names">the Matrix
specification</a>.</p>
<h2 id="delegation-faq"><a class="header" href="#delegation-faq">Delegation FAQ</a></h2>
<h3 id="when-do-i-need-delegation"><a class="header" href="#when-do-i-need-delegation">When do I need delegation?</a></h3>
<p>If your homeserver's APIs are accessible on the default federation port (8448)
and the domain your <code>server_name</code> points to, you do not need any delegation.</p>
<p>For instance, if you registered <code>example.com</code> and pointed its DNS A record at a
fresh server, you could install Synapse on that host, giving it a <code>server_name</code>
of <code>example.com</code>, and once a reverse proxy has been set up to proxy all requests
sent to the port <code>8448</code> and serve TLS certificates for <code>example.com</code>, you
wouldn't need any delegation set up.</p>
<p><strong>However</strong>, if your homeserver's APIs aren't accessible on port 8448 and on the
domain <code>server_name</code> points to, you will need to let other servers know how to
find it using delegation.</p>
<h3 id="do-you-still-recommend-against-using-a-reverse-proxy-on-the-federation-port"><a class="header" href="#do-you-still-recommend-against-using-a-reverse-proxy-on-the-federation-port">Do you still recommend against using a reverse proxy on the federation port?</a></h3>
<p>We no longer actively recommend against using a reverse proxy. Many admins will
find it easier to direct federation traffic to a reverse proxy and manage their
own TLS certificates, and this is a supported configuration.</p>
<p>See <a href="reverse_proxy.html">the reverse proxy documentation</a> for information on setting up a
reverse proxy.</p>
<h3 id="do-i-still-need-to-give-my-tls-certificates-to-synapse-if-i-am-using-a-reverse-proxy"><a class="header" href="#do-i-still-need-to-give-my-tls-certificates-to-synapse-if-i-am-using-a-reverse-proxy">Do I still need to give my TLS certificates to Synapse if I am using a reverse proxy?</a></h3>
<p>This is no longer necessary. If you are using a reverse proxy for all of your
TLS traffic, then you can set <code>no_tls: True</code> in the Synapse config.</p>
<p>In that case, the only reason Synapse needs the certificate is to populate a legacy
<code>tls_fingerprints</code> field in the federation API. This is ignored by Synapse 0.99.0
and later, and the only time pre-0.99 Synapses will check it is when attempting to
fetch the server keys - and generally this is delegated via <code>matrix.org</code>, which
is running a modern version of Synapse.</p>
<h3 id="do-i-need-the-same-certificate-for-the-client-and-federation-port"><a class="header" href="#do-i-need-the-same-certificate-for-the-client-and-federation-port">Do I need the same certificate for the client and federation port?</a></h3>
<p>No. There is nothing stopping you from using different certificates,
particularly if you are using a reverse proxy.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="upgrading-synapse"><a class="header" href="#upgrading-synapse">Upgrading Synapse</a></h1>
<p>Before upgrading check if any special steps are required to upgrade from
the version you currently have installed to the current version of
Synapse. The extra instructions that may be required are listed later in
this document.</p>
<ul>
<li>
<p>Check that your versions of Python and PostgreSQL are still
supported.</p>
<p>Synapse follows upstream lifecycles for <a href="https://endoflife.date/python">Python</a> and
<a href="https://endoflife.date/postgresql">PostgreSQL</a>, and removes support for versions
which are no longer maintained.</p>
<p>The website <a href="https://endoflife.date">https://endoflife.date</a> also offers convenient
summaries.</p>
</li>
<li>
<p>If Synapse was installed using <a href="setup/installation.html#prebuilt-packages">prebuilt
packages</a>, you will need to follow the
normal process for upgrading those packages.</p>
</li>
<li>
<p>If Synapse was installed from source, then:</p>
<ol>
<li>
<p>Activate the virtualenv before upgrading. For example, if
Synapse is installed in a virtualenv in <code>~/synapse/env</code> then
run:</p>
<pre><code class="language-bash">source ~/synapse/env/bin/activate
</code></pre>
</li>
<li>
<p>If Synapse was installed using pip then upgrade to the latest
version by running:</p>
<pre><code class="language-bash">pip install --upgrade matrix-synapse
</code></pre>
<p>If Synapse was installed using git then upgrade to the latest
version by running:</p>
<pre><code class="language-bash">git pull
pip install --upgrade .
</code></pre>
</li>
<li>
<p>Restart Synapse:</p>
<pre><code class="language-bash">./synctl restart
</code></pre>
</li>
</ol>
</li>
</ul>
<p>To check whether your update was successful, you can check the running
server version with:</p>
<pre><code class="language-bash"># you may need to replace 'localhost:8008' if synapse is not configured
# to listen on port 8008.
curl http://localhost:8008/_synapse/admin/v1/server_version
</code></pre>
<h2 id="rolling-back-to-older-versions"><a class="header" href="#rolling-back-to-older-versions">Rolling back to older versions</a></h2>
<p>Rolling back to previous releases can be difficult, due to database
schema changes between releases. Where we have been able to test the
rollback process, this will be noted below.</p>
<p>In general, you will need to undo any changes made during the upgrade
process, for example:</p>
<ul>
<li>
<p>pip:</p>
<pre><code class="language-bash">source env/bin/activate
# replace `1.3.0` accordingly:
pip install matrix-synapse==1.3.0
</code></pre>
</li>
<li>
<p>Debian:</p>
<pre><code class="language-bash"># replace `1.3.0` and `stretch` accordingly:
wget https://packages.matrix.org/debian/pool/main/m/matrix-synapse-py3/matrix-synapse-py3_1.3.0+stretch1_amd64.deb
dpkg -i matrix-synapse-py3_1.3.0+stretch1_amd64.deb
</code></pre>
</li>
</ul>
<h1 id="upgrading-to-v1390"><a class="header" href="#upgrading-to-v1390">Upgrading to v1.39.0</a></h1>
<h2 id="deprecation-of-the-current-third-party-rules-module-interface"><a class="header" href="#deprecation-of-the-current-third-party-rules-module-interface">Deprecation of the current third-party rules module interface</a></h2>
<p>The current third-party rules module interface is deprecated in favour of the new generic
modules system introduced in Synapse v1.37.0. Authors of third-party rules modules can refer
to <a href="modules.html#porting-an-existing-module-that-uses-the-old-interface">this documentation</a>
to update their modules. Synapse administrators can refer to <a href="modules.html#using-modules">this documentation</a>
to update their configuration once the modules they are using have been updated.</p>
<p>We plan to remove support for the current third-party rules interface in September 2021.</p>
<h1 id="upgrading-to-v1380"><a class="header" href="#upgrading-to-v1380">Upgrading to v1.38.0</a></h1>
<h2 id="re-indexing-of-events-table-on-postgres-databases"><a class="header" href="#re-indexing-of-events-table-on-postgres-databases">Re-indexing of <code>events</code> table on Postgres databases</a></h2>
<p>This release includes a database schema update which requires re-indexing one of
the larger tables in the database, <code>events</code>. This could result in increased
disk I/O for several hours or days after upgrading while the migration
completes. Furthermore, because we have to keep the old indexes until the new
indexes are ready, it could result in a significant, temporary, increase in
disk space.</p>
<p>To get a rough idea of the disk space required, check the current size of one
of the indexes. For example, from a <code>psql</code> shell, run the following sql:</p>
<pre><code class="language-sql">SELECT pg_size_pretty(pg_relation_size('events_order_room'));
</code></pre>
<p>We need to rebuild <strong>four</strong> indexes, so you will need to multiply this result
by four to give an estimate of the disk space required. For example, on one
particular server:</p>
<pre><code>synapse=# select pg_size_pretty(pg_relation_size('events_order_room'));
pg_size_pretty
----------------
288 MB
(1 row)
</code></pre>
<p>On this server, it would be wise to ensure that at least 1152MB are free.</p>
<p>The additional disk space will be freed once the migration completes.</p>
<p>SQLite databases are unaffected by this change.</p>
<h1 id="upgrading-to-v1370"><a class="header" href="#upgrading-to-v1370">Upgrading to v1.37.0</a></h1>
<h2 id="deprecation-of-the-current-spam-checker-interface"><a class="header" href="#deprecation-of-the-current-spam-checker-interface">Deprecation of the current spam checker interface</a></h2>
<p>The current spam checker interface is deprecated in favour of a new generic modules system.
Authors of spam checker modules can refer to <a href="https://matrix-org.github.io/synapse/develop/modules.html#porting-an-existing-module-that-uses-the-old-interface">this
documentation</a>
to update their modules. Synapse administrators can refer to <a href="https://matrix-org.github.io/synapse/develop/modules.html#using-modules">this
documentation</a>
to update their configuration once the modules they are using have been updated.</p>
<p>We plan to remove support for the current spam checker interface in August 2021.</p>
<p>More module interfaces will be ported over to this new generic system in future versions
of Synapse.</p>
<h1 id="upgrading-to-v1340"><a class="header" href="#upgrading-to-v1340">Upgrading to v1.34.0</a></h1>
<h2 id="room_invite_state_types-configuration-setting"><a class="header" href="#room_invite_state_types-configuration-setting"><code>room_invite_state_types</code> configuration setting</a></h2>
<p>The <code>room_invite_state_types</code> configuration setting has been deprecated
and replaced with <code>room_prejoin_state</code>. See the <a href="https://github.com/matrix-org/synapse/blob/v1.34.0/docs/sample_config.yaml#L1515">sample configuration
file</a>.</p>
<p>If you have set <code>room_invite_state_types</code> to the default value you
should simply remove it from your configuration file. The default value
used to be:</p>
<pre><code class="language-yaml">room_invite_state_types:
- "m.room.join_rules"
- "m.room.canonical_alias"
- "m.room.avatar"
- "m.room.encryption"
- "m.room.name"
</code></pre>
<p>If you have customised this value, you should remove
<code>room_invite_state_types</code> and configure <code>room_prejoin_state</code> instead.</p>
<h1 id="upgrading-to-v1330"><a class="header" href="#upgrading-to-v1330">Upgrading to v1.33.0</a></h1>
<h2 id="account-validity-html-templates-can-now-display-a-users-expiration-date"><a class="header" href="#account-validity-html-templates-can-now-display-a-users-expiration-date">Account Validity HTML templates can now display a user's expiration date</a></h2>
<p>This may affect you if you have enabled the account validity feature,
and have made use of a custom HTML template specified by the
<code>account_validity.template_dir</code> or
<code>account_validity.account_renewed_html_path</code> Synapse config options.</p>
<p>The template can now accept an <code>expiration_ts</code> variable, which
represents the unix timestamp in milliseconds for the future date of
which their account has been renewed until. See the <a href="https://github.com/matrix-org/synapse/blob/release-v1.33.0/synapse/res/templates/account_renewed.html">default
template</a>
for an example of usage.</p>
<p>ALso note that a new HTML template, <code>account_previously_renewed.html</code>,
has been added. This is is shown to users when they attempt to renew
their account with a valid renewal token that has already been used
before. The default template contents can been found
<a href="https://github.com/matrix-org/synapse/blob/release-v1.33.0/synapse/res/templates/account_previously_renewed.html">here</a>,
and can also accept an <code>expiration_ts</code> variable. This template replaces
the error message users would previously see upon attempting to use a
valid renewal token more than once.</p>
<h1 id="upgrading-to-v1320"><a class="header" href="#upgrading-to-v1320">Upgrading to v1.32.0</a></h1>
<h2 id="regression-causing-connected-prometheus-instances-to-become-overwhelmed"><a class="header" href="#regression-causing-connected-prometheus-instances-to-become-overwhelmed">Regression causing connected Prometheus instances to become overwhelmed</a></h2>
<p>This release introduces <a href="https://github.com/matrix-org/synapse/issues/9853">a
regression</a> that can
overwhelm connected Prometheus instances. This issue is not present in
Synapse v1.32.0rc1.</p>
<p>If you have been affected, please downgrade to 1.31.0. You then may need
to remove excess writeahead logs in order for Prometheus to recover.
Instructions for doing so are provided
<a href="https://github.com/matrix-org/synapse/pull/9854#issuecomment-823472183">here</a>.</p>
<h2 id="dropping-support-for-old-python-postgres-and-sqlite-versions"><a class="header" href="#dropping-support-for-old-python-postgres-and-sqlite-versions">Dropping support for old Python, Postgres and SQLite versions</a></h2>
<p>In line with our <a href="https://github.com/matrix-org/synapse/blob/release-v1.32.0/docs/deprecation_policy.md">deprecation
policy</a>,
we've dropped support for Python 3.5 and PostgreSQL 9.5, as they are no
longer supported upstream.</p>
<p>This release of Synapse requires Python 3.6+ and PostgresSQL 9.6+ or
SQLite 3.22+.</p>
<h2 id="removal-of-old-list-accounts-admin-api"><a class="header" href="#removal-of-old-list-accounts-admin-api">Removal of old List Accounts Admin API</a></h2>
<p>The deprecated v1 "list accounts" admin API
(<code>GET /_synapse/admin/v1/users/<user_id></code>) has been removed in this
version.</p>
<p>The <a href="https://github.com/matrix-org/synapse/blob/master/docs/admin_api/user_admin_api.rst#list-accounts">v2 list accounts
API</a>
has been available since Synapse 1.7.0 (2019-12-13), and is accessible
under <code>GET /_synapse/admin/v2/users</code>.</p>
<p>The deprecation of the old endpoint was announced with Synapse 1.28.0
(released on 2021-02-25).</p>
<h2 id="application-services-must-use-type-mloginapplication_service-when-registering-users"><a class="header" href="#application-services-must-use-type-mloginapplication_service-when-registering-users">Application Services must use type <code>m.login.application_service</code> when registering users</a></h2>
<p>In compliance with the <a href="https://matrix.org/docs/spec/application_service/r0.1.2#server-admin-style-permissions">Application Service
spec</a>,
Application Services are now required to use the
<code>m.login.application_service</code> type when registering users via the
<code>/_matrix/client/r0/register</code> endpoint. This behaviour was deprecated in
Synapse v1.30.0.</p>
<p>Please ensure your Application Services are up to date.</p>
<h1 id="upgrading-to-v1290"><a class="header" href="#upgrading-to-v1290">Upgrading to v1.29.0</a></h1>
<h2 id="requirement-for-x-forwarded-proto-header"><a class="header" href="#requirement-for-x-forwarded-proto-header">Requirement for X-Forwarded-Proto header</a></h2>
<p>When using Synapse with a reverse proxy (in particular, when using the
[x_forwarded]{.title-ref} option on an HTTP listener), Synapse now
expects to receive an [X-Forwarded-Proto]{.title-ref} header on incoming
HTTP requests. If it is not set, Synapse will log a warning on each
received request.</p>
<p>To avoid the warning, administrators using a reverse proxy should ensure
that the reverse proxy sets [X-Forwarded-Proto]{.title-ref} header to
[https]{.title-ref} or [http]{.title-ref} to indicate the protocol used
by the client.</p>
<p>Synapse also requires the [Host]{.title-ref} header to be preserved.</p>
<p>See the <a href="../reverse_proxy.html">reverse proxy documentation</a>, where the
example configurations have been updated to show how to set these
headers.</p>
<p>(Users of <a href="https://caddyserver.com/">Caddy</a> are unaffected, since we
believe it sets [X-Forwarded-Proto]{.title-ref} by default.)</p>
<h1 id="upgrading-to-v1270"><a class="header" href="#upgrading-to-v1270">Upgrading to v1.27.0</a></h1>
<h2 id="changes-to-callback-uri-for-oauth2--openid-connect-and-saml2"><a class="header" href="#changes-to-callback-uri-for-oauth2--openid-connect-and-saml2">Changes to callback URI for OAuth2 / OpenID Connect and SAML2</a></h2>
<p>This version changes the URI used for callbacks from OAuth2 and SAML2
identity providers:</p>
<ul>
<li>
<p>If your server is configured for single sign-on via an OpenID
Connect or OAuth2 identity provider, you will need to add
<code>[synapse public baseurl]/_synapse/client/oidc/callback</code> to the list
of permitted "redirect URIs" at the identity provider.</p>
<p>See the <a href="../openid.html">OpenID docs</a> for more information on setting
up OpenID Connect.</p>
</li>
<li>
<p>If your server is configured for single sign-on via a SAML2 identity
provider, you will need to add
<code>[synapse public baseurl]/_synapse/client/saml2/authn_response</code> as a
permitted "ACS location" (also known as "allowed callback URLs")
at the identity provider.</p>
<p>The "Issuer" in the "AuthnRequest" to the SAML2 identity
provider is also updated to
<code>[synapse public baseurl]/_synapse/client/saml2/metadata.xml</code>. If
your SAML2 identity provider uses this property to validate or
otherwise identify Synapse, its configuration will need to be
updated to use the new URL. Alternatively you could create a new,
separate "EntityDescriptor" in your SAML2 identity provider with
the new URLs and leave the URLs in the existing "EntityDescriptor"
as they were.</p>
</li>
</ul>
<h2 id="changes-to-html-templates"><a class="header" href="#changes-to-html-templates">Changes to HTML templates</a></h2>
<p>The HTML templates for SSO and email notifications now have <a href="https://jinja.palletsprojects.com/en/2.11.x/api/#autoescaping">Jinja2's
autoescape</a>
enabled for files ending in <code>.html</code>, <code>.htm</code>, and <code>.xml</code>. If you have
customised these templates and see issues when viewing them you might
need to update them. It is expected that most configurations will need
no changes.</p>
<p>If you have customised the templates <em>names</em> for these templates, it is
recommended to verify they end in <code>.html</code> to ensure autoescape is
enabled.</p>
<p>The above applies to the following templates:</p>
<ul>
<li><code>add_threepid.html</code></li>
<li><code>add_threepid_failure.html</code></li>
<li><code>add_threepid_success.html</code></li>
<li><code>notice_expiry.html</code></li>
<li><code>notice_expiry.html</code></li>
<li><code>notif_mail.html</code> (which, by default, includes <code>room.html</code> and
<code>notif.html</code>)</li>
<li><code>password_reset.html</code></li>
<li><code>password_reset_confirmation.html</code></li>
<li><code>password_reset_failure.html</code></li>
<li><code>password_reset_success.html</code></li>
<li><code>registration.html</code></li>
<li><code>registration_failure.html</code></li>
<li><code>registration_success.html</code></li>
<li><code>sso_account_deactivated.html</code></li>
<li><code>sso_auth_bad_user.html</code></li>
<li><code>sso_auth_confirm.html</code></li>
<li><code>sso_auth_success.html</code></li>
<li><code>sso_error.html</code></li>
<li><code>sso_login_idp_picker.html</code></li>
<li><code>sso_redirect_confirm.html</code></li>
</ul>
<h1 id="upgrading-to-v1260"><a class="header" href="#upgrading-to-v1260">Upgrading to v1.26.0</a></h1>
<h2 id="rolling-back-to-v1250-after-a-failed-upgrade"><a class="header" href="#rolling-back-to-v1250-after-a-failed-upgrade">Rolling back to v1.25.0 after a failed upgrade</a></h2>
<p>v1.26.0 includes a lot of large changes. If something problematic
occurs, you may want to roll-back to a previous version of Synapse.
Because v1.26.0 also includes a new database schema version, reverting
that version is also required alongside the generic rollback
instructions mentioned above. In short, to roll back to v1.25.0 you need
to:</p>
<ol>
<li>
<p>Stop the server</p>
</li>
<li>
<p>Decrease the schema version in the database:</p>
<pre><code class="language-sql">UPDATE schema_version SET version = 58;
</code></pre>
</li>
<li>
<p>Delete the ignored users & chain cover data:</p>
<pre><code class="language-sql">DROP TABLE IF EXISTS ignored_users;
UPDATE rooms SET has_auth_chain_index = false;
</code></pre>
<p>For PostgreSQL run:</p>
<pre><code class="language-sql">TRUNCATE event_auth_chain_links;
TRUNCATE event_auth_chains;
</code></pre>
<p>For SQLite run:</p>
<pre><code class="language-sql">DELETE FROM event_auth_chain_links;
DELETE FROM event_auth_chains;
</code></pre>
</li>
<li>
<p>Mark the deltas as not run (so they will re-run on upgrade).</p>
<pre><code class="language-sql">DELETE FROM applied_schema_deltas WHERE version = 59 AND file = "59/01ignored_user.py";
DELETE FROM applied_schema_deltas WHERE version = 59 AND file = "59/06chain_cover_index.sql";
</code></pre>
</li>
<li>
<p>Downgrade Synapse by following the instructions for your
installation method in the "Rolling back to older versions"
section above.</p>
</li>
</ol>
<h1 id="upgrading-to-v1250"><a class="header" href="#upgrading-to-v1250">Upgrading to v1.25.0</a></h1>
<h2 id="last-release-supporting-python-35"><a class="header" href="#last-release-supporting-python-35">Last release supporting Python 3.5</a></h2>
<p>This is the last release of Synapse which guarantees support with Python
3.5, which passed its upstream End of Life date several months ago.</p>
<p>We will attempt to maintain support through March 2021, but without
guarantees.</p>
<p>In the future, Synapse will follow upstream schedules for ending support
of older versions of Python and PostgreSQL. Please upgrade to at least
Python 3.6 and PostgreSQL 9.6 as soon as possible.</p>
<h2 id="blacklisting-ip-ranges"><a class="header" href="#blacklisting-ip-ranges">Blacklisting IP ranges</a></h2>
<p>Synapse v1.25.0 includes new settings, <code>ip_range_blacklist</code> and
<code>ip_range_whitelist</code>, for controlling outgoing requests from Synapse for
federation, identity servers, push, and for checking key validity for
third-party invite events. The previous setting,
<code>federation_ip_range_blacklist</code>, is deprecated. The new
<code>ip_range_blacklist</code> defaults to private IP ranges if it is not defined.</p>
<p>If you have never customised <code>federation_ip_range_blacklist</code> it is
recommended that you remove that setting.</p>
<p>If you have customised <code>federation_ip_range_blacklist</code> you should update
the setting name to <code>ip_range_blacklist</code>.</p>
<p>If you have a custom push server that is reached via private IP space
you may need to customise <code>ip_range_blacklist</code> or <code>ip_range_whitelist</code>.</p>
<h1 id="upgrading-to-v1240"><a class="header" href="#upgrading-to-v1240">Upgrading to v1.24.0</a></h1>
<h2 id="custom-openid-connect-mapping-provider-breaking-change"><a class="header" href="#custom-openid-connect-mapping-provider-breaking-change">Custom OpenID Connect mapping provider breaking change</a></h2>
<p>This release allows the OpenID Connect mapping provider to perform
normalisation of the localpart of the Matrix ID. This allows for the
mapping provider to specify different algorithms, instead of the
<a href="https://matrix.org/docs/spec/appendices#mapping-from-other-character-sets">default
way</a>.</p>
<p>If your Synapse configuration uses a custom mapping provider
([oidc_config.user_mapping_provider.module]{.title-ref} is specified and
not equal to
[synapse.handlers.oidc_handler.JinjaOidcMappingProvider]{.title-ref})
then you <em>must</em> ensure that [map_user_attributes]{.title-ref} of the
mapping provider performs some normalisation of the
[localpart]{.title-ref} returned. To match previous behaviour you can
use the [map_username_to_mxid_localpart]{.title-ref} function provided
by Synapse. An example is shown below:</p>
<pre><code class="language-python">from synapse.types import map_username_to_mxid_localpart
class MyMappingProvider:
def map_user_attributes(self, userinfo, token):
# ... your custom logic ...
sso_user_id = ...
localpart = map_username_to_mxid_localpart(sso_user_id)
return {"localpart": localpart}
</code></pre>
<h2 id="removal-historical-synapse-admin-api"><a class="header" href="#removal-historical-synapse-admin-api">Removal historical Synapse Admin API</a></h2>
<p>Historically, the Synapse Admin API has been accessible under:</p>
<ul>
<li><code>/_matrix/client/api/v1/admin</code></li>
<li><code>/_matrix/client/unstable/admin</code></li>
<li><code>/_matrix/client/r0/admin</code></li>
<li><code>/_synapse/admin/v1</code></li>
</ul>
<p>The endpoints with <code>/_matrix/client/*</code> prefixes have been removed as of
v1.24.0. The Admin API is now only accessible under:</p>
<ul>
<li><code>/_synapse/admin/v1</code></li>
</ul>
<p>The only exception is the [/admin/whois]{.title-ref} endpoint, which is
<a href="https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-admin-whois-userid">also available via the client-server
API</a>.</p>
<p>The deprecation of the old endpoints was announced with Synapse 1.20.0
(released on 2020-09-22) and makes it easier for homeserver admins to
lock down external access to the Admin API endpoints.</p>
<h1 id="upgrading-to-v1230"><a class="header" href="#upgrading-to-v1230">Upgrading to v1.23.0</a></h1>
<h2 id="structured-logging-configuration-breaking-changes"><a class="header" href="#structured-logging-configuration-breaking-changes">Structured logging configuration breaking changes</a></h2>
<p>This release deprecates use of the <code>structured: true</code> logging
configuration for structured logging. If your logging configuration
contains <code>structured: true</code> then it should be modified based on the
<a href="../structured_logging.html">structured logging
documentation</a>.</p>
<p>The <code>structured</code> and <code>drains</code> logging options are now deprecated and
should be replaced by standard logging configuration of <code>handlers</code> and
<code>formatters</code>.</p>
<p>A future will release of Synapse will make using <code>structured: true</code> an
error.</p>
<h1 id="upgrading-to-v1220"><a class="header" href="#upgrading-to-v1220">Upgrading to v1.22.0</a></h1>
<h2 id="thirdpartyeventrules-breaking-changes"><a class="header" href="#thirdpartyeventrules-breaking-changes">ThirdPartyEventRules breaking changes</a></h2>
<p>This release introduces a backwards-incompatible change to modules
making use of <code>ThirdPartyEventRules</code> in Synapse. If you make use of a
module defined under the <code>third_party_event_rules</code> config option, please
make sure it is updated to handle the below change:</p>
<p>The <code>http_client</code> argument is no longer passed to modules as they are
initialised. Instead, modules are expected to make use of the
<code>http_client</code> property on the <code>ModuleApi</code> class. Modules are now passed
a <code>module_api</code> argument during initialisation, which is an instance of
<code>ModuleApi</code>. <code>ModuleApi</code> instances have a <code>http_client</code> property which
acts the same as the <code>http_client</code> argument previously passed to
<code>ThirdPartyEventRules</code> modules.</p>
<h1 id="upgrading-to-v1210"><a class="header" href="#upgrading-to-v1210">Upgrading to v1.21.0</a></h1>
<h2 id="forwarding-_synapseclient-through-your-reverse-proxy"><a class="header" href="#forwarding-_synapseclient-through-your-reverse-proxy">Forwarding <code>/_synapse/client</code> through your reverse proxy</a></h2>
<p>The <a href="https://github.com/matrix-org/synapse/blob/develop/docs/reverse_proxy.md">reverse proxy
documentation</a>
has been updated to include reverse proxy directives for
<code>/_synapse/client/*</code> endpoints. As the user password reset flow now uses
endpoints under this prefix, <strong>you must update your reverse proxy
configurations for user password reset to work</strong>.</p>
<p>Additionally, note that the <a href="https://github.com/matrix-org/synapse/blob/develop/docs/workers.md">Synapse worker documentation</a> has been updated to</p>
<p>: state that the <code>/_synapse/client/password_reset/email/submit_token</code>
endpoint can be handled</p>
<p>by all workers. If you make use of Synapse's worker feature, please
update your reverse proxy configuration to reflect this change.</p>
<h2 id="new-html-templates"><a class="header" href="#new-html-templates">New HTML templates</a></h2>
<p>A new HTML template,
<a href="https://github.com/matrix-org/synapse/blob/develop/synapse/res/templates/password_reset_confirmation.html">password_reset_confirmation.html</a>,
has been added to the <code>synapse/res/templates</code> directory. If you are
using a custom template directory, you may want to copy the template
over and modify it.</p>
<p>Note that as of v1.20.0, templates do not need to be included in custom
template directories for Synapse to start. The default templates will be
used if a custom template cannot be found.</p>
<p>This page will appear to the user after clicking a password reset link
that has been emailed to them.</p>
<p>To complete password reset, the page must include a way to make a
[POST]{.title-ref} request to
<code>/_synapse/client/password_reset/{medium}/submit_token</code> with the query
parameters from the original link, presented as a URL-encoded form. See
the file itself for more details.</p>
<h2 id="updated-single-sign-on-html-templates"><a class="header" href="#updated-single-sign-on-html-templates">Updated Single Sign-on HTML Templates</a></h2>
<p>The <code>saml_error.html</code> template was removed from Synapse and replaced
with the <code>sso_error.html</code> template. If your Synapse is configured to use
SAML and a custom <code>sso_redirect_confirm_template_dir</code> configuration then
any customisations of the <code>saml_error.html</code> template will need to be
merged into the <code>sso_error.html</code> template. These templates are similar,
but the parameters are slightly different:</p>
<ul>
<li>The <code>msg</code> parameter should be renamed to <code>error_description</code>.</li>
<li>There is no longer a <code>code</code> parameter for the response code.</li>
<li>A string <code>error</code> parameter is available that includes a short hint
of why a user is seeing the error page.</li>
</ul>
<h1 id="upgrading-to-v1180"><a class="header" href="#upgrading-to-v1180">Upgrading to v1.18.0</a></h1>
<h2 id="docker--py3title-ref-suffix-will-be-removed-in-future-versions"><a class="header" href="#docker--py3title-ref-suffix-will-be-removed-in-future-versions">Docker [-py3]{.title-ref} suffix will be removed in future versions</a></h2>
<p>From 10th August 2020, we will no longer publish Docker images with the
[-py3]{.title-ref} tag suffix. The images tagged with the
[-py3]{.title-ref} suffix have been identical to the non-suffixed tags
since release 0.99.0, and the suffix is obsolete.</p>
<p>On 10th August, we will remove the [latest-py3]{.title-ref} tag.
Existing per-release tags (such as [v1.18.0-py3]{.title-ref}) will not
be removed, but no new [-py3]{.title-ref} tags will be added.</p>
<p>Scripts relying on the [-py3]{.title-ref} suffix will need to be
updated.</p>
<h2 id="redis-replication-is-now-recommended-in-lieu-of-tcp-replication"><a class="header" href="#redis-replication-is-now-recommended-in-lieu-of-tcp-replication">Redis replication is now recommended in lieu of TCP replication</a></h2>
<p>When setting up worker processes, we now recommend the use of a Redis
server for replication. <strong>The old direct TCP connection method is
deprecated and will be removed in a future release.</strong> See
<a href="../workers.html">workers</a> for more details.</p>
<h1 id="upgrading-to-v1140"><a class="header" href="#upgrading-to-v1140">Upgrading to v1.14.0</a></h1>
<p>This version includes a database update which is run as part of the
upgrade, and which may take a couple of minutes in the case of a large
server. Synapse will not respond to HTTP requests while this update is
taking place.</p>
<h1 id="upgrading-to-v1130"><a class="header" href="#upgrading-to-v1130">Upgrading to v1.13.0</a></h1>
<h2 id="incorrect-database-migration-in-old-synapse-versions"><a class="header" href="#incorrect-database-migration-in-old-synapse-versions">Incorrect database migration in old synapse versions</a></h2>
<p>A bug was introduced in Synapse 1.4.0 which could cause the room
directory to be incomplete or empty if Synapse was upgraded directly
from v1.2.1 or earlier, to versions between v1.4.0 and v1.12.x.</p>
<p>This will <em>not</em> be a problem for Synapse installations which were:</p>
<p>: - created at v1.4.0 or later,
- upgraded via v1.3.x, or
- upgraded straight from v1.2.1 or earlier to v1.13.0 or later.</p>
<p>If completeness of the room directory is a concern, installations which
are affected can be repaired as follows:</p>
<ol>
<li>
<p>Run the following sql from a [psql]{.title-ref} or
[sqlite3]{.title-ref} console:</p>
<pre><code class="language-sql">INSERT INTO background_updates (update_name, progress_json, depends_on) VALUES
('populate_stats_process_rooms', '{}', 'current_state_events_membership');
INSERT INTO background_updates (update_name, progress_json, depends_on) VALUES
('populate_stats_process_users', '{}', 'populate_stats_process_rooms');
</code></pre>
</li>
<li>
<p>Restart synapse.</p>
</li>
</ol>
<h2 id="new-single-sign-on-html-templates"><a class="header" href="#new-single-sign-on-html-templates">New Single Sign-on HTML Templates</a></h2>
<p>New templates (<code>sso_auth_confirm.html</code>, <code>sso_auth_success.html</code>, and
<code>sso_account_deactivated.html</code>) were added to Synapse. If your Synapse
is configured to use SSO and a custom
<code>sso_redirect_confirm_template_dir</code> configuration then these templates
will need to be copied from
<a href="synapse/res/templates">synapse/res/templates</a> into that directory.</p>
<h2 id="synapse-sso-plugins-method-deprecation"><a class="header" href="#synapse-sso-plugins-method-deprecation">Synapse SSO Plugins Method Deprecation</a></h2>
<p>Plugins using the <code>complete_sso_login</code> method of
<code>synapse.module_api.ModuleApi</code> should update to using the async/await
version <code>complete_sso_login_async</code> which includes additional checks. The
non-async version is considered deprecated.</p>
<h2 id="rolling-back-to-v1124-after-a-failed-upgrade"><a class="header" href="#rolling-back-to-v1124-after-a-failed-upgrade">Rolling back to v1.12.4 after a failed upgrade</a></h2>
<p>v1.13.0 includes a lot of large changes. If something problematic
occurs, you may want to roll-back to a previous version of Synapse.
Because v1.13.0 also includes a new database schema version, reverting
that version is also required alongside the generic rollback
instructions mentioned above. In short, to roll back to v1.12.4 you need
to:</p>
<ol>
<li>
<p>Stop the server</p>
</li>
<li>
<p>Decrease the schema version in the database:</p>
<pre><code class="language-sql">UPDATE schema_version SET version = 57;
</code></pre>
</li>
<li>
<p>Downgrade Synapse by following the instructions for your
installation method in the "Rolling back to older versions"
section above.</p>
</li>
</ol>
<h1 id="upgrading-to-v1120"><a class="header" href="#upgrading-to-v1120">Upgrading to v1.12.0</a></h1>
<p>This version includes a database update which is run as part of the
upgrade, and which may take some time (several hours in the case of a
large server). Synapse will not respond to HTTP requests while this
update is taking place.</p>
<p>This is only likely to be a problem in the case of a server which is
participating in many rooms.</p>
<ol start="0">
<li>
<p>As with all upgrades, it is recommended that you have a recent
backup of your database which can be used for recovery in the event
of any problems.</p>
</li>
<li>
<p>As an initial check to see if you will be affected, you can try
running the following query from the [psql]{.title-ref} or
[sqlite3]{.title-ref} console. It is safe to run it while Synapse is
still running.</p>
<pre><code class="language-sql">SELECT MAX(q.v) FROM (
SELECT (
SELECT ej.json AS v
FROM state_events se INNER JOIN event_json ej USING (event_id)
WHERE se.room_id=rooms.room_id AND se.type='m.room.create' AND se.state_key=''
LIMIT 1
) FROM rooms WHERE rooms.room_version IS NULL
) q;
</code></pre>
<p>This query will take about the same amount of time as the upgrade
process: ie, if it takes 5 minutes, then it is likely that Synapse
will be unresponsive for 5 minutes during the upgrade.</p>
<p>If you consider an outage of this duration to be acceptable, no
further action is necessary and you can simply start Synapse 1.12.0.</p>
<p>If you would prefer to reduce the downtime, continue with the steps
below.</p>
</li>
<li>
<p>The easiest workaround for this issue is to manually create a new
index before upgrading. On PostgreSQL, his can be done as follows:</p>
<pre><code class="language-sql">CREATE INDEX CONCURRENTLY tmp_upgrade_1_12_0_index
ON state_events(room_id) WHERE type = 'm.room.create';
</code></pre>
<p>The above query may take some time, but is also safe to run while
Synapse is running.</p>
<p>We assume that no SQLite users have databases large enough to be
affected. If you <em>are</em> affected, you can run a similar query,
omitting the <code>CONCURRENTLY</code> keyword. Note however that this
operation may in itself cause Synapse to stop running for some time.
Synapse admins are reminded that <a href="https://github.com/matrix-org/synapse/blob/master/README.rst#using-postgresql">SQLite is not recommended for use
outside a test
environment</a>.</p>
</li>
<li>
<p>Once the index has been created, the <code>SELECT</code> query in step 1 above
should complete quickly. It is therefore safe to upgrade to Synapse
1.12.0.</p>
</li>
<li>
<p>Once Synapse 1.12.0 has successfully started and is responding to
HTTP requests, the temporary index can be removed:</p>
<pre><code class="language-sql">DROP INDEX tmp_upgrade_1_12_0_index;
</code></pre>
</li>
</ol>
<h1 id="upgrading-to-v1100"><a class="header" href="#upgrading-to-v1100">Upgrading to v1.10.0</a></h1>
<p>Synapse will now log a warning on start up if used with a PostgreSQL
database that has a non-recommended locale set.</p>
<p>See <a href="../postgres.html">Postgres</a> for details.</p>
<h1 id="upgrading-to-v180"><a class="header" href="#upgrading-to-v180">Upgrading to v1.8.0</a></h1>
<p>Specifying a <code>log_file</code> config option will now cause Synapse to refuse
to start, and should be replaced by with the <code>log_config</code> option.
Support for the <code>log_file</code> option was removed in v1.3.0 and has since
had no effect.</p>
<h1 id="upgrading-to-v170"><a class="header" href="#upgrading-to-v170">Upgrading to v1.7.0</a></h1>
<p>In an attempt to configure Synapse in a privacy preserving way, the
default behaviours of <code>allow_public_rooms_without_auth</code> and
<code>allow_public_rooms_over_federation</code> have been inverted. This means that
by default, only authenticated users querying the Client/Server API will
be able to query the room directory, and relatedly that the server will
not share room directory information with other servers over federation.</p>
<p>If your installation does not explicitly set these settings one way or
the other and you want either setting to be <code>true</code> then it will
necessary to update your homeserver configuration file accordingly.</p>
<p>For more details on the surrounding context see our
<a href="https://matrix.org/blog/2019/11/09/avoiding-unwelcome-visitors-on-private-matrix-servers">explainer</a>.</p>
<h1 id="upgrading-to-v150"><a class="header" href="#upgrading-to-v150">Upgrading to v1.5.0</a></h1>
<p>This release includes a database migration which may take several
minutes to complete if there are a large number (more than a million or
so) of entries in the <code>devices</code> table. This is only likely to a be a
problem on very large installations.</p>
<h1 id="upgrading-to-v140"><a class="header" href="#upgrading-to-v140">Upgrading to v1.4.0</a></h1>
<h2 id="new-custom-templates"><a class="header" href="#new-custom-templates">New custom templates</a></h2>
<p>If you have configured a custom template directory with the
<code>email.template_dir</code> option, be aware that there are new templates
regarding registration and threepid management (see below) that must be
included.</p>
<ul>
<li><code>registration.html</code> and <code>registration.txt</code></li>
<li><code>registration_success.html</code> and <code>registration_failure.html</code></li>
<li><code>add_threepid.html</code> and <code>add_threepid.txt</code></li>
<li><code>add_threepid_failure.html</code> and <code>add_threepid_success.html</code></li>
</ul>
<p>Synapse will expect these files to exist inside the configured template
directory, and <strong>will fail to start</strong> if they are absent. To view the
default templates, see
<a href="https://github.com/matrix-org/synapse/tree/master/synapse/res/templates">synapse/res/templates</a>.</p>
<h2 id="3pid-verification-changes"><a class="header" href="#3pid-verification-changes">3pid verification changes</a></h2>
<p><strong>Note: As of this release, users will be unable to add phone numbers or
email addresses to their accounts, without changes to the Synapse
configuration. This includes adding an email address during
registration.</strong></p>
<p>It is possible for a user to associate an email address or phone number
with their account, for a number of reasons:</p>
<ul>
<li>for use when logging in, as an alternative to the user id.</li>
<li>in the case of email, as an alternative contact to help with account
recovery.</li>
<li>in the case of email, to receive notifications of missed messages.</li>
</ul>
<p>Before an email address or phone number can be added to a user's
account, or before such an address is used to carry out a
password-reset, Synapse must confirm the operation with the owner of the
email address or phone number. It does this by sending an email or text
giving the user a link or token to confirm receipt. This process is
known as '3pid verification'. ('3pid', or 'threepid', stands for
third-party identifier, and we use it to refer to external identifiers
such as email addresses and phone numbers.)</p>
<p>Previous versions of Synapse delegated the task of 3pid verification to
an identity server by default. In most cases this server is <code>vector.im</code>
or <code>matrix.org</code>.</p>
<p>In Synapse 1.4.0, for security and privacy reasons, the homeserver will
no longer delegate this task to an identity server by default. Instead,
the server administrator will need to explicitly decide how they would
like the verification messages to be sent.</p>
<p>In the medium term, the <code>vector.im</code> and <code>matrix.org</code> identity servers
will disable support for delegated 3pid verification entirely. However,
in order to ease the transition, they will retain the capability for a
limited period. Delegated email verification will be disabled on Monday
2nd December 2019 (giving roughly 2 months notice). Disabling delegated
SMS verification will follow some time after that once SMS verification
support lands in Synapse.</p>
<p>Once delegated 3pid verification support has been disabled in the
<code>vector.im</code> and <code>matrix.org</code> identity servers, all Synapse versions that
depend on those instances will be unable to verify email and phone
numbers through them. There are no imminent plans to remove delegated
3pid verification from Sydent generally. (Sydent is the identity server
project that backs the <code>vector.im</code> and <code>matrix.org</code> instances).</p>
<h3 id="email-1"><a class="header" href="#email-1">Email</a></h3>
<p>Following upgrade, to continue verifying email (e.g. as part of the
registration process), admins can either:-</p>
<ul>
<li>Configure Synapse to use an email server.</li>
<li>Run or choose an identity server which allows delegated email
verification and delegate to it.</li>
</ul>
<h4 id="configure-smtp-in-synapse"><a class="header" href="#configure-smtp-in-synapse">Configure SMTP in Synapse</a></h4>
<p>To configure an SMTP server for Synapse, modify the configuration
section headed <code>email</code>, and be sure to have at least the
<code>smtp_host, smtp_port</code> and <code>notif_from</code> fields filled out.</p>
<p>You may also need to set <code>smtp_user</code>, <code>smtp_pass</code>, and
<code>require_transport_security</code>.</p>
<p>See the <a href="docs/sample_config.yaml">sample configuration file</a> for more
details on these settings.</p>
<h4 id="delegate-email-to-an-identity-server"><a class="header" href="#delegate-email-to-an-identity-server">Delegate email to an identity server</a></h4>
<p>Some admins will wish to continue using email verification as part of
the registration process, but will not immediately have an appropriate
SMTP server at hand.</p>
<p>To this end, we will continue to support email verification delegation
via the <code>vector.im</code> and <code>matrix.org</code> identity servers for two months.
Support for delegated email verification will be disabled on Monday 2nd
December.</p>
<p>The <code>account_threepid_delegates</code> dictionary defines whether the
homeserver should delegate an external server (typically an <a href="https://matrix.org/docs/spec/identity_service/r0.2.1">identity
server</a>) to handle
sending confirmation messages via email and SMS.</p>
<p>So to delegate email verification, in <code>homeserver.yaml</code>, set
<code>account_threepid_delegates.email</code> to the base URL of an identity
server. For example:</p>
<pre><code class="language-yaml">account_threepid_delegates:
email: https://example.com # Delegate email sending to example.com
</code></pre>
<p>Note that <code>account_threepid_delegates.email</code> replaces the deprecated
<code>email.trust_identity_server_for_password_resets</code>: if
<code>email.trust_identity_server_for_password_resets</code> is set to <code>true</code>, and
<code>account_threepid_delegates.email</code> is not set, then the first entry in
<code>trusted_third_party_id_servers</code> will be used as the
<code>account_threepid_delegate</code> for email. This is to ensure compatibility
with existing Synapse installs that set up external server handling for
these tasks before v1.4.0. If
<code>email.trust_identity_server_for_password_resets</code> is <code>true</code> and no
trusted identity server domains are configured, Synapse will report an
error and refuse to start.</p>
<p>If <code>email.trust_identity_server_for_password_resets</code> is <code>false</code> or
absent and no <code>email</code> delegate is configured in
<code>account_threepid_delegates</code>, then Synapse will send email verification
messages itself, using the configured SMTP server (see above). that
type.</p>
<h3 id="phone-numbers"><a class="header" href="#phone-numbers">Phone numbers</a></h3>
<p>Synapse does not support phone-number verification itself, so the only
way to maintain the ability for users to add phone numbers to their
accounts will be by continuing to delegate phone number verification to
the <code>matrix.org</code> and <code>vector.im</code> identity servers (or another identity
server that supports SMS sending).</p>
<p>The <code>account_threepid_delegates</code> dictionary defines whether the
homeserver should delegate an external server (typically an <a href="https://matrix.org/docs/spec/identity_service/r0.2.1">identity
server</a>) to handle
sending confirmation messages via email and SMS.</p>
<p>So to delegate phone number verification, in <code>homeserver.yaml</code>, set
<code>account_threepid_delegates.msisdn</code> to the base URL of an identity
server. For example:</p>
<pre><code class="language-yaml">account_threepid_delegates:
msisdn: https://example.com # Delegate sms sending to example.com
</code></pre>
<p>The <code>matrix.org</code> and <code>vector.im</code> identity servers will continue to
support delegated phone number verification via SMS until such time as
it is possible for admins to configure their servers to perform phone
number verification directly. More details will follow in a future
release.</p>
<h2 id="rolling-back-to-v131"><a class="header" href="#rolling-back-to-v131">Rolling back to v1.3.1</a></h2>
<p>If you encounter problems with v1.4.0, it should be possible to roll
back to v1.3.1, subject to the following:</p>
<ul>
<li>
<p>The 'room statistics' engine was heavily reworked in this release
(see <a href="https://github.com/matrix-org/synapse/pull/5971">#5971</a>),
including significant changes to the database schema, which are not
easily reverted. This will cause the room statistics engine to stop
updating when you downgrade.</p>
<p>The room statistics are essentially unused in v1.3.1 (in future
versions of Synapse, they will be used to populate the room
directory), so there should be no loss of functionality. However,
the statistics engine will write errors to the logs, which can be
avoided by setting the following in <code>homeserver.yaml</code>:</p>
<pre><code class="language-yaml">stats:
enabled: false
</code></pre>
<p>Don't forget to re-enable it when you upgrade again, in preparation
for its use in the room directory!</p>
</li>
</ul>
<h1 id="upgrading-to-v120"><a class="header" href="#upgrading-to-v120">Upgrading to v1.2.0</a></h1>
<p>Some counter metrics have been renamed, with the old names deprecated.
See <a href="../metrics-howto.html#renaming-of-metrics--deprecation-of-old-names-in-12">the metrics
documentation</a>
for details.</p>
<h1 id="upgrading-to-v110"><a class="header" href="#upgrading-to-v110">Upgrading to v1.1.0</a></h1>
<p>Synapse v1.1.0 removes support for older Python and PostgreSQL versions,
as outlined in <a href="https://matrix.org/blog/2019/04/08/synapse-deprecating-postgres-9-4-and-python-2-x">our deprecation
notice</a>.</p>
<h2 id="minimum-python-version"><a class="header" href="#minimum-python-version">Minimum Python Version</a></h2>
<p>Synapse v1.1.0 has a minimum Python requirement of Python 3.5. Python
3.6 or Python 3.7 are recommended as they have improved internal string
handling, significantly reducing memory usage.</p>
<p>If you use current versions of the Matrix.org-distributed Debian
packages or Docker images, action is not required.</p>
<p>If you install Synapse in a Python virtual environment, please see
"Upgrading to v0.34.0" for notes on setting up a new virtualenv under
Python 3.</p>
<h2 id="minimum-postgresql-version"><a class="header" href="#minimum-postgresql-version">Minimum PostgreSQL Version</a></h2>
<p>If using PostgreSQL under Synapse, you will need to use PostgreSQL 9.5
or above. Please see the <a href="https://www.postgresql.org/docs/11/upgrading.html">PostgreSQL
documentation</a> for
more details on upgrading your database.</p>
<h1 id="upgrading-to-v10"><a class="header" href="#upgrading-to-v10">Upgrading to v1.0</a></h1>
<h2 id="validation-of-tls-certificates"><a class="header" href="#validation-of-tls-certificates">Validation of TLS certificates</a></h2>
<p>Synapse v1.0 is the first release to enforce validation of TLS
certificates for the federation API. It is therefore essential that your
certificates are correctly configured. See the
<a href="../MSC1711_certificates_FAQ.html">FAQ</a> for more information.</p>
<p>Note, v1.0 installations will also no longer be able to federate with
servers that have not correctly configured their certificates.</p>
<p>In rare cases, it may be desirable to disable certificate checking: for
example, it might be essential to be able to federate with a given
legacy server in a closed federation. This can be done in one of two
ways:-</p>
<ul>
<li>Configure the global switch <code>federation_verify_certificates</code> to
<code>false</code>.</li>
<li>Configure a whitelist of server domains to trust via
<code>federation_certificate_verification_whitelist</code>.</li>
</ul>
<p>See the <a href="docs/sample_config.yaml">sample configuration file</a> for more
details on these settings.</p>
<h2 id="email-2"><a class="header" href="#email-2">Email</a></h2>
<p>When a user requests a password reset, Synapse will send an email to the
user to confirm the request.</p>
<p>Previous versions of Synapse delegated the job of sending this email to
an identity server. If the identity server was somehow malicious or
became compromised, it would be theoretically possible to hijack an
account through this means.</p>
<p>Therefore, by default, Synapse v1.0 will send the confirmation email
itself. If Synapse is not configured with an SMTP server, password reset
via email will be disabled.</p>
<p>To configure an SMTP server for Synapse, modify the configuration
section headed <code>email</code>, and be sure to have at least the <code>smtp_host</code>,
<code>smtp_port</code> and <code>notif_from</code> fields filled out. You may also need to set
<code>smtp_user</code>, <code>smtp_pass</code>, and <code>require_transport_security</code>.</p>
<p>If you are absolutely certain that you wish to continue using an
identity server for password resets, set
<code>trust_identity_server_for_password_resets</code> to <code>true</code>.</p>
<p>See the <a href="docs/sample_config.yaml">sample configuration file</a> for more
details on these settings.</p>
<h2 id="new-email-templates"><a class="header" href="#new-email-templates">New email templates</a></h2>
<p>Some new templates have been added to the default template directory for the purpose of
the homeserver sending its own password reset emails. If you have configured a
custom <code>template_dir</code> in your Synapse config, these files will need to be added.</p>
<p><code>password_reset.html</code> and <code>password_reset.txt</code> are HTML and plain text
templates respectively that contain the contents of what will be emailed
to the user upon attempting to reset their password via email.
<code>password_reset_success.html</code> and <code>password_reset_failure.html</code> are HTML
files that the content of which (assuming no redirect URL is set) will
be shown to the user after they attempt to click the link in the email
sent to them.</p>
<h1 id="upgrading-to-v0990"><a class="header" href="#upgrading-to-v0990">Upgrading to v0.99.0</a></h1>
<p>Please be aware that, before Synapse v1.0 is released around March 2019,
you will need to replace any self-signed certificates with those
verified by a root CA. Information on how to do so can be found at <a href="../ACME.html">the
ACME docs</a>.</p>
<p>For more information on configuring TLS certificates see the
<a href="../MSC1711_certificates_FAQ.html">FAQ</a>.</p>
<h1 id="upgrading-to-v0340"><a class="header" href="#upgrading-to-v0340">Upgrading to v0.34.0</a></h1>
<ol>
<li>
<p>This release is the first to fully support Python 3. Synapse will
now run on Python versions 3.5, or 3.6 (as well as 2.7). We
recommend switching to Python 3, as it has been shown to give
performance improvements.</p>
<p>For users who have installed Synapse into a virtualenv, we recommend
doing this by creating a new virtualenv. For example:</p>
<pre><code>virtualenv -p python3 ~/synapse/env3
source ~/synapse/env3/bin/activate
pip install matrix-synapse
</code></pre>
<p>You can then start synapse as normal, having activated the new
virtualenv:</p>
<pre><code>cd ~/synapse
source env3/bin/activate
synctl start
</code></pre>
<p>Users who have installed from distribution packages should see the
relevant package documentation. See below for notes on Debian
packages.</p>
<ul>
<li>
<p>When upgrading to Python 3, you <strong>must</strong> make sure that your log
files are configured as UTF-8, by adding <code>encoding: utf8</code> to the
<code>RotatingFileHandler</code> configuration (if you have one) in your
<code><server>.log.config</code> file. For example, if your <code>log.config</code>
file contains:</p>
<pre><code>handlers:
file:
class: logging.handlers.RotatingFileHandler
formatter: precise
filename: homeserver.log
maxBytes: 104857600
backupCount: 10
filters: [context]
console:
class: logging.StreamHandler
formatter: precise
filters: [context]
</code></pre>
<p>Then you should update this to be:</p>
<pre><code>handlers:
file:
class: logging.handlers.RotatingFileHandler
formatter: precise
filename: homeserver.log
maxBytes: 104857600
backupCount: 10
filters: [context]
encoding: utf8
console:
class: logging.StreamHandler
formatter: precise
filters: [context]
</code></pre>
<p>There is no need to revert this change if downgrading to
Python 2.</p>
</li>
</ul>
<p>We are also making available Debian packages which will run Synapse
on Python 3. You can switch to these packages with
<code>apt-get install matrix-synapse-py3</code>, however, please read
<a href="https://github.com/matrix-org/synapse/blob/release-v0.34.0/debian/NEWS">debian/NEWS</a>
before doing so. The existing <code>matrix-synapse</code> packages will
continue to use Python 2 for the time being.</p>
</li>
<li>
<p>This release removes the <code>riot.im</code> from the default list of trusted
identity servers.</p>
<p>If <code>riot.im</code> is in your homeserver's list of
<code>trusted_third_party_id_servers</code>, you should remove it. It was added
in case a hypothetical future identity server was put there. If you
don't remove it, users may be unable to deactivate their accounts.</p>
</li>
<li>
<p>This release no longer installs the (unmaintained) Matrix Console
web client as part of the default installation. It is possible to
re-enable it by installing it separately and setting the
<code>web_client_location</code> config option, but please consider switching
to another client.</p>
</li>
</ol>
<h1 id="upgrading-to-v0337"><a class="header" href="#upgrading-to-v0337">Upgrading to v0.33.7</a></h1>
<p>This release removes the example email notification templates from
<code>res/templates</code> (they are now internal to the python package). This
should only affect you if you (a) deploy your Synapse instance from a
git checkout or a github snapshot URL, and (b) have email notifications
enabled.</p>
<p>If you have email notifications enabled, you should ensure that
<code>email.template_dir</code> is either configured to point at a directory where
you have installed customised templates, or leave it unset to use the
default templates.</p>
<h1 id="upgrading-to-v0273"><a class="header" href="#upgrading-to-v0273">Upgrading to v0.27.3</a></h1>
<p>This release expands the anonymous usage stats sent if the opt-in
<code>report_stats</code> configuration is set to <code>true</code>. We now capture RSS memory
and cpu use at a very coarse level. This requires administrators to
install the optional <code>psutil</code> python module.</p>
<p>We would appreciate it if you could assist by ensuring this module is
available and <code>report_stats</code> is enabled. This will let us see if
performance changes to synapse are having an impact to the general
community.</p>
<h1 id="upgrading-to-v0150"><a class="header" href="#upgrading-to-v0150">Upgrading to v0.15.0</a></h1>
<p>If you want to use the new URL previewing API
(<code>/_matrix/media/r0/preview_url</code>) then you have to explicitly enable it
in the config and update your dependencies dependencies. See README.rst
for details.</p>
<h1 id="upgrading-to-v0110"><a class="header" href="#upgrading-to-v0110">Upgrading to v0.11.0</a></h1>
<p>This release includes the option to send anonymous usage stats to
matrix.org, and requires that administrators explictly opt in or out by
setting the <code>report_stats</code> option to either <code>true</code> or <code>false</code>.</p>
<p>We would really appreciate it if you could help our project out by
reporting anonymized usage statistics from your homeserver. Only very
basic aggregate data (e.g. number of users) will be reported, but it
helps us to track the growth of the Matrix community, and helps us to
make Matrix a success, as well as to convince other networks that they
should peer with us.</p>
<h1 id="upgrading-to-v090"><a class="header" href="#upgrading-to-v090">Upgrading to v0.9.0</a></h1>
<p>Application services have had a breaking API change in this version.</p>
<p>They can no longer register themselves with a home server using the AS
HTTP API. This decision was made because a compromised application
service with free reign to register any regex in effect grants full
read/write access to the home server if a regex of <code>.*</code> is used. An
attack where a compromised AS re-registers itself with <code>.*</code> was deemed
too big of a security risk to ignore, and so the ability to register
with the HS remotely has been removed.</p>
<p>It has been replaced by specifying a list of application service
registrations in <code>homeserver.yaml</code>:</p>
<pre><code>app_service_config_files: ["registration-01.yaml", "registration-02.yaml"]
</code></pre>
<p>Where <code>registration-01.yaml</code> looks like:</p>
<pre><code>url: <String> # e.g. "https://my.application.service.com"
as_token: <String>
hs_token: <String>
sender_localpart: <String> # This is a new field which denotes the user_id localpart when using the AS token
namespaces:
users:
- exclusive: <Boolean>
regex: <String> # e.g. "@prefix_.*"
aliases:
- exclusive: <Boolean>
regex: <String>
rooms:
- exclusive: <Boolean>
regex: <String>
</code></pre>
<h1 id="upgrading-to-v080"><a class="header" href="#upgrading-to-v080">Upgrading to v0.8.0</a></h1>
<p>Servers which use captchas will need to add their public key to:</p>
<pre><code>static/client/register/register_config.js
window.matrixRegistrationConfig = {
recaptcha_public_key: "YOUR_PUBLIC_KEY"
};
</code></pre>
<p>This is required in order to support registration fallback (typically
used on mobile devices).</p>
<h1 id="upgrading-to-v070"><a class="header" href="#upgrading-to-v070">Upgrading to v0.7.0</a></h1>
<p>New dependencies are:</p>
<ul>
<li>pydenticon</li>
<li>simplejson</li>
<li>syutil</li>
<li>matrix-angular-sdk</li>
</ul>
<p>To pull in these dependencies in a virtual env, run:</p>
<pre><code>python synapse/python_dependencies.py | xargs -n 1 pip install
</code></pre>
<h1 id="upgrading-to-v060"><a class="header" href="#upgrading-to-v060">Upgrading to v0.6.0</a></h1>
<p>To pull in new dependencies, run:</p>
<pre><code>python setup.py develop --user
</code></pre>
<p>This update includes a change to the database schema. To upgrade you
first need to upgrade the database by running:</p>
<pre><code>python scripts/upgrade_db_to_v0.6.0.py <db> <server_name> <signing_key>
</code></pre>
<p>Where [<db>]{.title-ref} is the location of the database,
[<server_name>]{.title-ref} is the server name as specified in the
synapse configuration, and [<signing_key>]{.title-ref} is the location
of the signing key as specified in the synapse configuration.</p>
<p>This may take some time to complete. Failures of signatures and content
hashes can safely be ignored.</p>
<h1 id="upgrading-to-v051"><a class="header" href="#upgrading-to-v051">Upgrading to v0.5.1</a></h1>
<p>Depending on precisely when you installed v0.5.0 you may have ended up
with a stale release of the reference matrix webclient installed as a
python module. To uninstall it and ensure you are depending on the
latest module, please run:</p>
<pre><code>$ pip uninstall syweb
</code></pre>
<h1 id="upgrading-to-v050"><a class="header" href="#upgrading-to-v050">Upgrading to v0.5.0</a></h1>
<p>The webclient has been split out into a seperate repository/pacakage in
this release. Before you restart your homeserver you will need to pull
in the webclient package by running:</p>
<pre><code>python setup.py develop --user
</code></pre>
<p>This release completely changes the database schema and so requires
upgrading it before starting the new version of the homeserver.</p>
<p>The script "database-prepare-for-0.5.0.sh" should be used to upgrade
the database. This will save all user information, such as logins and
profiles, but will otherwise purge the database. This includes messages,
which rooms the home server was a member of and room alias mappings.</p>
<p>If you would like to keep your history, please take a copy of your
database file and ask for help in #matrix:matrix.org. The upgrade
process is, unfortunately, non trivial and requires human intervention
to resolve any resulting conflicts during the upgrade process.</p>
<p>Before running the command the homeserver should be first completely
shutdown. To run it, simply specify the location of the database, e.g.:</p>
<blockquote>
<p>./scripts/database-prepare-for-0.5.0.sh "homeserver.db"</p>
</blockquote>
<p>Once this has successfully completed it will be safe to restart the
homeserver. You may notice that the homeserver takes a few seconds
longer to restart than usual as it reinitializes the database.</p>
<p>On startup of the new version, users can either rejoin remote rooms
using room aliases or by being reinvited. Alternatively, if any other
homeserver sends a message to a room that the homeserver was previously
in the local HS will automatically rejoin the room.</p>
<h1 id="upgrading-to-v040"><a class="header" href="#upgrading-to-v040">Upgrading to v0.4.0</a></h1>
<p>This release needs an updated syutil version. Run:</p>
<pre><code>python setup.py develop
</code></pre>
<p>You will also need to upgrade your configuration as the signing key
format has changed. Run:</p>
<pre><code>python -m synapse.app.homeserver --config-path <CONFIG> --generate-config
</code></pre>
<h1 id="upgrading-to-v030"><a class="header" href="#upgrading-to-v030">Upgrading to v0.3.0</a></h1>
<p>This registration API now closely matches the login API. This introduces
a bit more backwards and forwards between the HS and the client, but
this improves the overall flexibility of the API. You can now GET on
/register to retrieve a list of valid registration flows. Upon choosing
one, they are submitted in the same way as login, e.g:</p>
<pre><code>{
type: m.login.password,
user: foo,
password: bar
}
</code></pre>
<p>The default HS supports 2 flows, with and without Identity Server email
authentication. Enabling captcha on the HS will add in an extra step to
all flows: <code>m.login.recaptcha</code> which must be completed before you can
transition to the next stage. There is a new login type:
<code>m.login.email.identity</code> which contains the <code>threepidCreds</code> key which
were previously sent in the original register request. For more
information on this, see the specification.</p>
<h2 id="web-client"><a class="header" href="#web-client">Web Client</a></h2>
<p>The VoIP specification has changed between v0.2.0 and v0.3.0. Users
should refresh any browser tabs to get the latest web client code. Users
on v0.2.0 of the web client will not be able to call those on v0.3.0 and
vice versa.</p>
<h1 id="upgrading-to-v020"><a class="header" href="#upgrading-to-v020">Upgrading to v0.2.0</a></h1>
<p>The home server now requires setting up of SSL config before it can run.
To automatically generate default config use:</p>
<pre><code>$ python synapse/app/homeserver.py \
--server-name machine.my.domain.name \
--bind-port 8448 \
--config-path homeserver.config \
--generate-config
</code></pre>
<p>This config can be edited if desired, for example to specify a different
SSL certificate to use. Once done you can run the home server using:</p>
<pre><code>$ python synapse/app/homeserver.py --config-path homeserver.config
</code></pre>
<p>See the README.rst for more information.</p>
<p>Also note that some config options have been renamed, including:</p>
<ul>
<li>"host" to "server-name"</li>
<li>"database" to "database-path"</li>
<li>"port" to "bind-port" and "unsecure-port"</li>
</ul>
<h1 id="upgrading-to-v001"><a class="header" href="#upgrading-to-v001">Upgrading to v0.0.1</a></h1>
<p>This release completely changes the database schema and so requires
upgrading it before starting the new version of the homeserver.</p>
<p>The script "database-prepare-for-0.0.1.sh" should be used to upgrade
the database. This will save all user information, such as logins and
profiles, but will otherwise purge the database. This includes messages,
which rooms the home server was a member of and room alias mappings.</p>
<p>Before running the command the homeserver should be first completely
shutdown. To run it, simply specify the location of the database, e.g.:</p>
<blockquote>
<p>./scripts/database-prepare-for-0.0.1.sh "homeserver.db"</p>
</blockquote>
<p>Once this has successfully completed it will be safe to restart the
homeserver. You may notice that the homeserver takes a few seconds
longer to restart than usual as it reinitializes the database.</p>
<p>On startup of the new version, users can either rejoin remote rooms
using room aliases or by being reinvited. Alternatively, if any other
homeserver sends a message to a room that the homeserver was previously
in the local HS will automatically rejoin the room.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="msc1711-certificates-faq"><a class="header" href="#msc1711-certificates-faq">MSC1711 Certificates FAQ</a></h1>
<h2 id="historical-note"><a class="header" href="#historical-note">Historical Note</a></h2>
<p>This document was originally written to guide server admins through the upgrade
path towards Synapse 1.0. Specifically,
<a href="https://github.com/matrix-org/matrix-doc/blob/master/proposals/1711-x509-for-federation.md">MSC1711</a>
required that all servers present valid TLS certificates on their federation
API. Admins were encouraged to achieve compliance from version 0.99.0 (released
in February 2019) ahead of version 1.0 (released June 2019) enforcing the
certificate checks.</p>
<p>Much of what follows is now outdated since most admins will have already
upgraded, however it may be of use to those with old installs returning to the
project.</p>
<p>If you are setting up a server from scratch you almost certainly should look at
the <a href="setup/installation.html">installation guide</a> instead.</p>
<h2 id="introduction-1"><a class="header" href="#introduction-1">Introduction</a></h2>
<p>The goal of Synapse 0.99.0 is to act as a stepping stone to Synapse 1.0.0. It
supports the r0.1 release of the server to server specification, but is
compatible with both the legacy Matrix federation behaviour (pre-r0.1) as well
as post-r0.1 behaviour, in order to allow for a smooth upgrade across the
federation.</p>
<p>The most important thing to know is that Synapse 1.0.0 will require a valid TLS
certificate on federation endpoints. Self signed certificates will not be
sufficient.</p>
<p>Synapse 0.99.0 makes it easy to configure TLS certificates and will
interoperate with both >= 1.0.0 servers as well as existing servers yet to
upgrade.</p>
<p><strong>It is critical that all admins upgrade to 0.99.0 and configure a valid TLS
certificate.</strong> Admins will have 1 month to do so, after which 1.0.0 will be
released and those servers without a valid certificate will not longer be able
to federate with >= 1.0.0 servers.</p>
<p>Full details on how to carry out this configuration change is given
<a href="MSC1711_certificates_FAQ.html#configuring-certificates-for-compatibility-with-synapse-100">below</a>. A
timeline and some frequently asked questions are also given below.</p>
<p>For more details and context on the release of the r0.1 Server/Server API and
imminent Matrix 1.0 release, you can also see our
<a href="https://matrix.org/blog/2019/02/04/matrix-at-fosdem-2019/">main talk from FOSDEM 2019</a>.</p>
<h2 id="contents"><a class="header" href="#contents">Contents</a></h2>
<ul>
<li>Timeline</li>
<li>Configuring certificates for compatibility with Synapse 1.0</li>
<li>FAQ
<ul>
<li>Synapse 0.99.0 has just been released, what do I need to do right now?</li>
<li>How do I upgrade?</li>
<li>What will happen if I do not set up a valid federation certificate
immediately?</li>
<li>What will happen if I do nothing at all?</li>
<li>When do I need a SRV record or .well-known URI?</li>
<li>Can I still use an SRV record?</li>
<li>I have created a .well-known URI. Do I still need an SRV record?</li>
<li>It used to work just fine, why are you breaking everything?</li>
<li>Can I manage my own certificates rather than having Synapse renew
certificates itself?</li>
<li>Do you still recommend against using a reverse proxy on the federation port?</li>
<li>Do I still need to give my TLS certificates to Synapse if I am using a
reverse proxy?</li>
<li>Do I need the same certificate for the client and federation port?</li>
<li>How do I tell Synapse to reload my keys/certificates after I replace them?</li>
</ul>
</li>
</ul>
<h2 id="timeline"><a class="header" href="#timeline">Timeline</a></h2>
<p><strong>5th Feb 2019 - Synapse 0.99.0 is released.</strong></p>
<p>All server admins are encouraged to upgrade.</p>
<p>0.99.0:</p>
<ul>
<li>
<p>provides support for ACME to make setting up Let's Encrypt certs easy, as
well as .well-known support.</p>
</li>
<li>
<p>does not enforce that a valid CA cert is present on the federation API, but
rather makes it easy to set one up.</p>
</li>
<li>
<p>provides support for .well-known</p>
</li>
</ul>
<p>Admins should upgrade and configure a valid CA cert. Homeservers that require a
.well-known entry (see below), should retain their SRV record and use it
alongside their .well-known record.</p>
<p><strong>10th June 2019 - Synapse 1.0.0 is released</strong></p>
<p>1.0.0 is scheduled for release on 10th June. In
accordance with the the <a href="https://matrix.org/docs/spec/server_server/r0.1.0.html">S2S spec</a>
1.0.0 will enforce certificate validity. This means that any homeserver without a
valid certificate after this point will no longer be able to federate with
1.0.0 servers.</p>
<h2 id="configuring-certificates-for-compatibility-with-synapse-100"><a class="header" href="#configuring-certificates-for-compatibility-with-synapse-100">Configuring certificates for compatibility with Synapse 1.0.0</a></h2>
<h3 id="if-you-do-not-currently-have-an-srv-record"><a class="header" href="#if-you-do-not-currently-have-an-srv-record">If you do not currently have an SRV record</a></h3>
<p>In this case, your <code>server_name</code> points to the host where your Synapse is
running. There is no need to create a <code>.well-known</code> URI or an SRV record, but
you will need to give Synapse a valid, signed, certificate.</p>
<h3 id="if-you-do-have-an-srv-record-currently"><a class="header" href="#if-you-do-have-an-srv-record-currently">If you do have an SRV record currently</a></h3>
<p>If you are using an SRV record, your matrix domain (<code>server_name</code>) may not
point to the same host that your Synapse is running on (the 'target
domain'). (If it does, you can follow the recommendation above; otherwise, read
on.)</p>
<p>Let's assume that your <code>server_name</code> is <code>example.com</code>, and your Synapse is
hosted at a target domain of <code>customer.example.net</code>. Currently you should have
an SRV record which looks like:</p>
<pre><code>_matrix._tcp.example.com. IN SRV 10 5 8000 customer.example.net.
</code></pre>
<p>In this situation, you have three choices for how to proceed:</p>
<h4 id="option-1-give-synapse-a-certificate-for-your-matrix-domain"><a class="header" href="#option-1-give-synapse-a-certificate-for-your-matrix-domain">Option 1: give Synapse a certificate for your matrix domain</a></h4>
<p>Synapse 1.0 will expect your server to present a TLS certificate for your
<code>server_name</code> (<code>example.com</code> in the above example). You can achieve this by acquiring a
certificate for the <code>server_name</code> yourself (for example, using <code>certbot</code>), and giving it
and the key to Synapse via <code>tls_certificate_path</code> and <code>tls_private_key_path</code>.</p>
<h4 id="option-2-run-synapse-behind-a-reverse-proxy"><a class="header" href="#option-2-run-synapse-behind-a-reverse-proxy">Option 2: run Synapse behind a reverse proxy</a></h4>
<p>If you have an existing reverse proxy set up with correct TLS certificates for
your domain, you can simply route all traffic through the reverse proxy by
updating the SRV record appropriately (or removing it, if the proxy listens on
8448).</p>
<p>See <a href="reverse_proxy.html">the reverse proxy documentation</a> for information on setting up a
reverse proxy.</p>
<h4 id="option-3-add-a-well-known-file-to-delegate-your-matrix-traffic"><a class="header" href="#option-3-add-a-well-known-file-to-delegate-your-matrix-traffic">Option 3: add a .well-known file to delegate your matrix traffic</a></h4>
<p>This will allow you to keep Synapse on a separate domain, without having to
give it a certificate for the matrix domain.</p>
<p>You can do this with a <code>.well-known</code> file as follows:</p>
<ol>
<li>
<p>Keep the SRV record in place - it is needed for backwards compatibility
with Synapse 0.34 and earlier.</p>
</li>
<li>
<p>Give Synapse a certificate corresponding to the target domain
(<code>customer.example.net</code> in the above example). You can do this by acquire a
certificate for the target domain and giving it to Synapse via <code>tls_certificate_path</code>
and <code>tls_private_key_path</code>.</p>
</li>
<li>
<p>Restart Synapse to ensure the new certificate is loaded.</p>
</li>
<li>
<p>Arrange for a <code>.well-known</code> file at
<code>https://<server_name>/.well-known/matrix/server</code> with contents:</p>
<pre><code class="language-json">{"m.server": "<target server name>"}
</code></pre>
<p>where the target server name is resolved as usual (i.e. SRV lookup, falling
back to talking to port 8448).</p>
<p>In the above example, where synapse is listening on port 8000,
<code>https://example.com/.well-known/matrix/server</code> should have <code>m.server</code> set to one of:</p>
<ol>
<li>
<p><code>customer.example.net</code> ─ with a SRV record on
<code>_matrix._tcp.customer.example.com</code> pointing to port 8000, or:</p>
</li>
<li>
<p><code>customer.example.net</code> ─ updating synapse to listen on the default port
8448, or:</p>
</li>
<li>
<p><code>customer.example.net:8000</code> ─ ensuring that if there is a reverse proxy
on <code>customer.example.net:8000</code> it correctly handles HTTP requests with
Host header set to <code>customer.example.net:8000</code>.</p>
</li>
</ol>
</li>
</ol>
<h2 id="faq"><a class="header" href="#faq">FAQ</a></h2>
<h3 id="synapse-0990-has-just-been-released-what-do-i-need-to-do-right-now"><a class="header" href="#synapse-0990-has-just-been-released-what-do-i-need-to-do-right-now">Synapse 0.99.0 has just been released, what do I need to do right now?</a></h3>
<p>Upgrade as soon as you can in preparation for Synapse 1.0.0, and update your
TLS certificates as <a href="MSC1711_certificates_FAQ.html#configuring-certificates-for-compatibility-with-synapse-100">above</a>.</p>
<h3 id="what-will-happen-if-i-do-not-set-up-a-valid-federation-certificate-immediately"><a class="header" href="#what-will-happen-if-i-do-not-set-up-a-valid-federation-certificate-immediately">What will happen if I do not set up a valid federation certificate immediately?</a></h3>
<p>Nothing initially, but once 1.0.0 is in the wild it will not be possible to
federate with 1.0.0 servers.</p>
<h3 id="what-will-happen-if-i-do-nothing-at-all"><a class="header" href="#what-will-happen-if-i-do-nothing-at-all">What will happen if I do nothing at all?</a></h3>
<p>If the admin takes no action at all, and remains on a Synapse < 0.99.0 then the
homeserver will be unable to federate with those who have implemented
.well-known. Then, as above, once the month upgrade window has expired the
homeserver will not be able to federate with any Synapse >= 1.0.0</p>
<h3 id="when-do-i-need-a-srv-record-or-well-known-uri"><a class="header" href="#when-do-i-need-a-srv-record-or-well-known-uri">When do I need a SRV record or .well-known URI?</a></h3>
<p>If your homeserver listens on the default federation port (8448), and your
<code>server_name</code> points to the host that your homeserver runs on, you do not need an
SRV record or <code>.well-known/matrix/server</code> URI.</p>
<p>For instance, if you registered <code>example.com</code> and pointed its DNS A record at a
fresh Upcloud VPS or similar, you could install Synapse 0.99 on that host,
giving it a server_name of <code>example.com</code>, and it would automatically generate a
valid TLS certificate for you via Let's Encrypt and no SRV record or
<code>.well-known</code> URI would be needed.</p>
<p>This is the common case, although you can add an SRV record or
<code>.well-known/matrix/server</code> URI for completeness if you wish.</p>
<p><strong>However</strong>, if your server does not listen on port 8448, or if your <code>server_name</code>
does not point to the host that your homeserver runs on, you will need to let
other servers know how to find it.</p>
<p>In this case, you should see <a href="MSC1711_certificates_FAQ.html#if-you-do-have-an-srv-record-currently">"If you do have an SRV record
currently"</a> above.</p>
<h3 id="can-i-still-use-an-srv-record"><a class="header" href="#can-i-still-use-an-srv-record">Can I still use an SRV record?</a></h3>
<p>Firstly, if you didn't need an SRV record before (because your server is
listening on port 8448 of your server_name), you certainly don't need one now:
the defaults are still the same.</p>
<p>If you previously had an SRV record, you can keep using it provided you are
able to give Synapse a TLS certificate corresponding to your server name. For
example, suppose you had the following SRV record, which directs matrix traffic
for example.com to matrix.example.com:443:</p>
<pre><code>_matrix._tcp.example.com. IN SRV 10 5 443 matrix.example.com
</code></pre>
<p>In this case, Synapse must be given a certificate for example.com - or be
configured to acquire one from Let's Encrypt.</p>
<p>If you are unable to give Synapse a certificate for your server_name, you will
also need to use a .well-known URI instead. However, see also "I have created a
.well-known URI. Do I still need an SRV record?".</p>
<h3 id="i-have-created-a-well-known-uri-do-i-still-need-an-srv-record"><a class="header" href="#i-have-created-a-well-known-uri-do-i-still-need-an-srv-record">I have created a .well-known URI. Do I still need an SRV record?</a></h3>
<p>As of Synapse 0.99, Synapse will first check for the existence of a <code>.well-known</code>
URI and follow any delegation it suggests. It will only then check for the
existence of an SRV record.</p>
<p>That means that the SRV record will often be redundant. However, you should
remember that there may still be older versions of Synapse in the federation
which do not understand <code>.well-known</code> URIs, so if you removed your SRV record you
would no longer be able to federate with them.</p>
<p>It is therefore best to leave the SRV record in place for now. Synapse 0.34 and
earlier will follow the SRV record (and not care about the invalid
certificate). Synapse 0.99 and later will follow the .well-known URI, with the
correct certificate chain.</p>
<h3 id="it-used-to-work-just-fine-why-are-you-breaking-everything"><a class="header" href="#it-used-to-work-just-fine-why-are-you-breaking-everything">It used to work just fine, why are you breaking everything?</a></h3>
<p>We have always wanted Matrix servers to be as easy to set up as possible, and
so back when we started federation in 2014 we didn't want admins to have to go
through the cumbersome process of buying a valid TLS certificate to run a
server. This was before Let's Encrypt came along and made getting a free and
valid TLS certificate straightforward. So instead, we adopted a system based on
<a href="https://en.wikipedia.org/wiki/Convergence_(SSL)">Perspectives</a>: an approach
where you check a set of "notary servers" (in practice, homeservers) to vouch
for the validity of a certificate rather than having it signed by a CA. As long
as enough different notaries agree on the certificate's validity, then it is
trusted.</p>
<p>However, in practice this has never worked properly. Most people only use the
default notary server (matrix.org), leading to inadvertent centralisation which
we want to eliminate. Meanwhile, we never implemented the full consensus
algorithm to query the servers participating in a room to determine consensus
on whether a given certificate is valid. This is fiddly to get right
(especially in face of sybil attacks), and we found ourselves questioning
whether it was worth the effort to finish the work and commit to maintaining a
secure certificate validation system as opposed to focusing on core Matrix
development.</p>
<p>Meanwhile, Let's Encrypt came along in 2016, and put the final nail in the
coffin of the Perspectives project (which was already pretty dead). So, the
Spec Core Team decided that a better approach would be to mandate valid TLS
certificates for federation alongside the rest of the Web. More details can be
found in
<a href="https://github.com/matrix-org/matrix-doc/blob/master/proposals/1711-x509-for-federation.md#background-the-failure-of-the-perspectives-approach">MSC1711</a>.</p>
<p>This results in a breaking change, which is disruptive, but absolutely critical
for the security model. However, the existence of Let's Encrypt as a trivial
way to replace the old self-signed certificates with valid CA-signed ones helps
smooth things over massively, especially as Synapse can now automate Let's
Encrypt certificate generation if needed.</p>
<h3 id="can-i-manage-my-own-certificates-rather-than-having-synapse-renew-certificates-itself"><a class="header" href="#can-i-manage-my-own-certificates-rather-than-having-synapse-renew-certificates-itself">Can I manage my own certificates rather than having Synapse renew certificates itself?</a></h3>
<p>Yes, you are welcome to manage your certificates yourself. Synapse will only
attempt to obtain certificates from Let's Encrypt if you configure it to do
so.The only requirement is that there is a valid TLS cert present for
federation end points.</p>
<h3 id="do-you-still-recommend-against-using-a-reverse-proxy-on-the-federation-port-1"><a class="header" href="#do-you-still-recommend-against-using-a-reverse-proxy-on-the-federation-port-1">Do you still recommend against using a reverse proxy on the federation port?</a></h3>
<p>We no longer actively recommend against using a reverse proxy. Many admins will
find it easier to direct federation traffic to a reverse proxy and manage their
own TLS certificates, and this is a supported configuration.</p>
<p>See <a href="reverse_proxy.html">the reverse proxy documentation</a> for information on setting up a
reverse proxy.</p>
<h3 id="do-i-still-need-to-give-my-tls-certificates-to-synapse-if-i-am-using-a-reverse-proxy-1"><a class="header" href="#do-i-still-need-to-give-my-tls-certificates-to-synapse-if-i-am-using-a-reverse-proxy-1">Do I still need to give my TLS certificates to Synapse if I am using a reverse proxy?</a></h3>
<p>Practically speaking, this is no longer necessary.</p>
<p>If you are using a reverse proxy for all of your TLS traffic, then you can set
<code>no_tls: True</code>. In that case, the only reason Synapse needs the certificate is
to populate a legacy 'tls_fingerprints' field in the federation API. This is
ignored by Synapse 0.99.0 and later, and the only time pre-0.99 Synapses will
check it is when attempting to fetch the server keys - and generally this is
delegated via <code>matrix.org</code>, which is on 0.99.0.</p>
<p>However, there is a bug in Synapse 0.99.0
<a href="https://github.com/matrix-org/synapse/issues/4554">4554</a> which prevents
Synapse from starting if you do not give it a TLS certificate. To work around
this, you can give it any TLS certificate at all. This will be fixed soon.</p>
<h3 id="do-i-need-the-same-certificate-for-the-client-and-federation-port-1"><a class="header" href="#do-i-need-the-same-certificate-for-the-client-and-federation-port-1">Do I need the same certificate for the client and federation port?</a></h3>
<p>No. There is nothing stopping you from using different certificates,
particularly if you are using a reverse proxy. However, Synapse will use the
same certificate on any ports where TLS is configured.</p>
<h3 id="how-do-i-tell-synapse-to-reload-my-keyscertificates-after-i-replace-them"><a class="header" href="#how-do-i-tell-synapse-to-reload-my-keyscertificates-after-i-replace-them">How do I tell Synapse to reload my keys/certificates after I replace them?</a></h3>
<p>Synapse will reload the keys and certificates when it receives a SIGHUP - for
example <code>kill -HUP $(cat homeserver.pid)</code>. Alternatively, simply restart
Synapse, though this will result in downtime while it restarts.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="setting-up-federation"><a class="header" href="#setting-up-federation">Setting up federation</a></h1>
<p>Federation is the process by which users on different servers can participate
in the same room. For this to work, those other servers must be able to contact
yours to send messages.</p>
<p>The <code>server_name</code> configured in the Synapse configuration file (often
<code>homeserver.yaml</code>) defines how resources (users, rooms, etc.) will be
identified (eg: <code>@user:example.com</code>, <code>#room:example.com</code>). By default,
it is also the domain that other servers will use to try to reach your
server (via port 8448). This is easy to set up and will work provided
you set the <code>server_name</code> to match your machine's public DNS hostname.</p>
<p>For this default configuration to work, you will need to listen for TLS
connections on port 8448. The preferred way to do that is by using a
reverse proxy: see <a href="reverse_proxy.html">the reverse proxy documentation</a> for instructions
on how to correctly set one up.</p>
<p>In some cases you might not want to run Synapse on the machine that has
the <code>server_name</code> as its public DNS hostname, or you might want federation
traffic to use a different port than 8448. For example, you might want to
have your user names look like <code>@user:example.com</code>, but you want to run
Synapse on <code>synapse.example.com</code> on port 443. This can be done using
delegation, which allows an admin to control where federation traffic should
be sent. See <a href="delegate.html">the delegation documentation</a> for instructions on how to set this up.</p>
<p>Once federation has been configured, you should be able to join a room over
federation. A good place to start is <code>#synapse:matrix.org</code> - a room for
Synapse admins.</p>
<h2 id="troubleshooting-2"><a class="header" href="#troubleshooting-2">Troubleshooting</a></h2>
<p>You can use the <a href="https://matrix.org/federationtester">federation tester</a>
to check if your homeserver is configured correctly. Alternatively try the
<a href="https://matrix.org/federationtester/api/report?server_name=DOMAIN">JSON API used by the federation tester</a>.
Note that you'll have to modify this URL to replace <code>DOMAIN</code> with your
<code>server_name</code>. Hitting the API directly provides extra detail.</p>
<p>The typical failure mode for federation is that when the server tries to join
a room, it is rejected with "401: Unauthorized". Generally this means that other
servers in the room could not access yours. (Joining a room over federation is
a complicated dance which requires connections in both directions).</p>
<p>Another common problem is that people on other servers can't join rooms that
you invite them to. This can be caused by an incorrectly-configured reverse
proxy: see <a href="reverse_proxy.html">the reverse proxy documentation</a> for instructions on how
to correctly configure a reverse proxy.</p>
<h3 id="known-issues"><a class="header" href="#known-issues">Known issues</a></h3>
<p><strong>HTTP <code>308 Permanent Redirect</code> redirects are not followed</strong>: Due to missing features
in the HTTP library used by Synapse, 308 redirects are currently not followed by
federating servers, which can cause <code>M_UNKNOWN</code> or <code>401 Unauthorized</code> errors. This
may affect users who are redirecting apex-to-www (e.g. <code>example.com</code> -> <code>www.example.com</code>),
and especially users of the Kubernetes <em>Nginx Ingress</em> module, which uses 308 redirect
codes by default. For those Kubernetes users, <a href="https://stackoverflow.com/a/52617528/5096871">this Stackoverflow post</a>
might be helpful. For other users, switching to a <code>301 Moved Permanently</code> code may be
an option. 308 redirect codes will be supported properly in a future
release of Synapse.</p>
<h2 id="running-a-demo-federation-of-synapses"><a class="header" href="#running-a-demo-federation-of-synapses">Running a demo federation of Synapses</a></h2>
<p>If you want to get up and running quickly with a trio of homeservers in a
private federation, there is a script in the <code>demo</code> directory. This is mainly
useful just for development purposes. See <a href="https://github.com/matrix-org/synapse/tree/develop/demo/">demo/README</a>.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="configuration-1"><a class="header" href="#configuration-1">Configuration</a></h1>
<p>This section contains information on tweaking Synapse via the various options in the configuration file. A configuration
file should have been generated when you <a href="usage/configuration/../../setup/installation.html">installed Synapse</a>.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="homeserver-sample-configuration-file"><a class="header" href="#homeserver-sample-configuration-file">Homeserver Sample Configuration File</a></h1>
<p>Below is a sample homeserver configuration file. The homeserver configuration file
can be tweaked to change the behaviour of your homeserver. A restart of the server is
generally required to apply any changes made to this file.</p>
<p>Note that the contents below are <em>not</em> intended to be copied and used as the basis for
a real homeserver.yaml. Instead, if you are starting from scratch, please generate
a fresh config using Synapse by following the instructions in
<a href="usage/configuration/../../setup/installation.html">Installation</a>.</p>
<pre><code class="language-yaml"># This file is maintained as an up-to-date snapshot of the default
# homeserver.yaml configuration generated by Synapse.
#
# It is intended to act as a reference for the default configuration,
# helping admins keep track of new options and other changes, and compare
# their configs with the current default. As such, many of the actual
# config values shown are placeholders.
#
# It is *not* intended to be copied and used as the basis for a real
# homeserver.yaml. Instead, if you are starting from scratch, please generate
# a fresh config using Synapse by following the instructions in
# https://matrix-org.github.io/synapse/latest/setup/installation.html.
# 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.
################################################################################
# Configuration file for Synapse.
#
# This is a YAML file: see [1] for a quick introduction. Note in particular
# that *indentation is important*: all the elements of a list or dictionary
# should have the same indentation.
#
# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
## Modules ##
# Server admins can expand Synapse's functionality with external modules.
#
# See https://matrix-org.github.io/synapse/latest/modules.html for more
# documentation on how to configure or create custom modules for Synapse.
#
modules:
# - module: my_super_module.MySuperClass
# config:
# do_thing: true
# - module: my_other_super_module.SomeClass
# config: {}
## Server ##
# The public-facing domain of the server
#
# The server_name name will appear at the end of usernames and room addresses
# created on this server. For example if the server_name was example.com,
# usernames on this server would be in the format @user:example.com
#
# In most cases you should avoid using a matrix specific subdomain such as
# matrix.example.com or synapse.example.com as the server_name for the same
# reasons you wouldn't use user@email.example.com as your email address.
# See https://matrix-org.github.io/synapse/latest/delegate.html
# for information on how to host Synapse on a subdomain while preserving
# a clean server_name.
#
# The server_name cannot be changed later so it is important to
# configure this correctly before you start Synapse. It should be all
# lowercase and may contain an explicit port.
# Examples: matrix.org, localhost:8080
#
server_name: "SERVERNAME"
# When running as a daemon, the file to store the pid in
#
pid_file: DATADIR/homeserver.pid
# The absolute URL to the web client which /_matrix/client will redirect
# to if 'webclient' is configured under the 'listeners' configuration.
#
# This option can be also set to the filesystem path to the web client
# which will be served at /_matrix/client/ if 'webclient' is configured
# under the 'listeners' configuration, however this is a security risk:
# https://github.com/matrix-org/synapse#security-note
#
#web_client_location: https://riot.example.com/
# The public-facing base URL that clients use to access this Homeserver (not
# including _matrix/...). This is the same URL a user might enter into the
# 'Custom Homeserver URL' field on their client. If you use Synapse with a
# reverse proxy, this should be the URL to reach Synapse via the proxy.
# Otherwise, it should be the URL to reach Synapse's client HTTP listener (see
# 'listeners' below).
#
#public_baseurl: https://example.com/
# Set the soft limit on the number of file descriptors synapse can use
# Zero is used to indicate synapse should set the soft limit to the
# hard limit.
#
#soft_file_limit: 0
# Presence tracking allows users to see the state (e.g online/offline)
# of other local and remote users.
#
presence:
# Uncomment to disable presence tracking on this homeserver. This option
# replaces the previous top-level 'use_presence' option.
#
#enabled: false
# Presence routers are third-party modules that can specify additional logic
# to where presence updates from users are routed.
#
presence_router:
# The custom module's class. Uncomment to use a custom presence router module.
#
#module: "my_custom_router.PresenceRouter"
# Configuration options of the custom module. Refer to your module's
# documentation for available options.
#
#config:
# example_option: 'something'
# Whether to require authentication to retrieve profile data (avatars,
# display names) of other users through the client API. Defaults to
# 'false'. Note that profile data is also available via the federation
# API, unless allow_profile_lookup_over_federation is set to false.
#
#require_auth_for_profile_requests: true
# Uncomment to require a user to share a room with another user in order
# to retrieve their profile information. Only checked on Client-Server
# requests. Profile requests from other servers should be checked by the
# requesting server. Defaults to 'false'.
#
#limit_profile_requests_to_users_who_share_rooms: true
# Uncomment to prevent a user's profile data from being retrieved and
# displayed in a room until they have joined it. By default, a user's
# profile data is included in an invite event, regardless of the values
# of the above two settings, and whether or not the users share a server.
# Defaults to 'true'.
#
#include_profile_data_on_invite: false
# If set to 'true', removes the need for authentication to access the server's
# public rooms directory through the client API, meaning that anyone can
# query the room directory. Defaults to 'false'.
#
#allow_public_rooms_without_auth: true
# If set to 'true', allows any other homeserver to fetch the server's public
# rooms directory via federation. Defaults to 'false'.
#
#allow_public_rooms_over_federation: true
# The default room version for newly created rooms.
#
# Known room versions are listed here:
# https://matrix.org/docs/spec/#complete-list-of-room-versions
#
# For example, for room version 1, default_room_version should be set
# to "1".
#
#default_room_version: "6"
# The GC threshold parameters to pass to `gc.set_threshold`, if defined
#
#gc_thresholds: [700, 10, 10]
# The minimum time in seconds between each GC for a generation, regardless of
# the GC thresholds. This ensures that we don't do GC too frequently.
#
# A value of `[1s, 10s, 30s]` indicates that a second must pass between consecutive
# generation 0 GCs, etc.
#
# Defaults to `[1s, 10s, 30s]`.
#
#gc_min_interval: [0.5s, 30s, 1m]
# Set the limit on the returned events in the timeline in the get
# and sync operations. The default value is 100. -1 means no upper limit.
#
# Uncomment the following to increase the limit to 5000.
#
#filter_timeline_limit: 5000
# Whether room invites to users on this server should be blocked
# (except those sent by local server admins). The default is False.
#
#block_non_admin_invites: true
# Room searching
#
# If disabled, new messages will not be indexed for searching and users
# will receive errors when searching for messages. Defaults to enabled.
#
#enable_search: false
# Prevent outgoing requests from being sent to the following blacklisted IP address
# CIDR ranges. If this option is not specified then it defaults to private IP
# address ranges (see the example below).
#
# The blacklist applies to the outbound requests for federation, identity servers,
# push servers, and for checking key validity for third-party invite events.
#
# (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly
# listed here, since they correspond to unroutable addresses.)
#
# This option replaces federation_ip_range_blacklist in Synapse v1.25.0.
#
#ip_range_blacklist:
# - '127.0.0.0/8'
# - '10.0.0.0/8'
# - '172.16.0.0/12'
# - '192.168.0.0/16'
# - '100.64.0.0/10'
# - '192.0.0.0/24'
# - '169.254.0.0/16'
# - '192.88.99.0/24'
# - '198.18.0.0/15'
# - '192.0.2.0/24'
# - '198.51.100.0/24'
# - '203.0.113.0/24'
# - '224.0.0.0/4'
# - '::1/128'
# - 'fe80::/10'
# - 'fc00::/7'
# - '2001:db8::/32'
# - 'ff00::/8'
# - 'fec0::/10'
# List of IP address CIDR ranges that should be allowed for federation,
# identity servers, push servers, and for checking key validity for
# third-party invite events. This is useful for specifying exceptions to
# wide-ranging blacklisted target IP ranges - e.g. for communication with
# a push server only visible in your network.
#
# This whitelist overrides ip_range_blacklist and defaults to an empty
# list.
#
#ip_range_whitelist:
# - '192.168.1.1'
# List of ports that Synapse should listen on, their purpose and their
# configuration.
#
# Options for each listener include:
#
# port: the TCP port to bind to
#
# bind_addresses: a list of local addresses to listen on. The default is
# 'all local interfaces'.
#
# type: the type of listener. Normally 'http', but other valid options are:
# 'manhole' (see https://matrix-org.github.io/synapse/latest/manhole.html),
# 'metrics' (see https://matrix-org.github.io/synapse/latest/metrics-howto.html),
# 'replication' (see https://matrix-org.github.io/synapse/latest/workers.html).
#
# tls: set to true to enable TLS for this listener. Will use the TLS
# key/cert specified in tls_private_key_path / tls_certificate_path.
#
# x_forwarded: Only valid for an 'http' listener. Set to true to use the
# X-Forwarded-For header as the client IP. Useful when Synapse is
# behind a reverse-proxy.
#
# resources: Only valid for an 'http' listener. A list of resources to host
# on this port. Options for each resource are:
#
# names: a list of names of HTTP resources. See below for a list of
# valid resource names.
#
# compress: set to true to enable HTTP compression for this resource.
#
# additional_resources: Only valid for an 'http' listener. A map of
# additional endpoints which should be loaded via dynamic modules.
#
# Valid resource names are:
#
# client: the client-server API (/_matrix/client), and the synapse admin
# API (/_synapse/admin). Also implies 'media' and 'static'.
#
# consent: user consent forms (/_matrix/consent).
# See https://matrix-org.github.io/synapse/latest/consent_tracking.html.
#
# federation: the server-server API (/_matrix/federation). Also implies
# 'media', 'keys', 'openid'
#
# keys: the key discovery API (/_matrix/keys).
#
# media: the media API (/_matrix/media).
#
# metrics: the metrics interface.
# See https://matrix-org.github.io/synapse/latest/metrics-howto.html.
#
# openid: OpenID authentication.
#
# replication: the HTTP replication API (/_synapse/replication).
# See https://matrix-org.github.io/synapse/latest/workers.html.
#
# static: static resources under synapse/static (/_matrix/static). (Mostly
# useful for 'fallback authentication'.)
#
# webclient: A web client. Requires web_client_location to be set.
#
listeners:
# TLS-enabled listener: for when matrix traffic is sent directly to synapse.
#
# Disabled by default. To enable it, uncomment the following. (Note that you
# will also need to give Synapse a TLS key and certificate: see the TLS section
# below.)
#
#- port: 8448
# type: http
# tls: true
# resources:
# - names: [client, federation]
# Unsecure HTTP listener: for when matrix traffic passes through a reverse proxy
# that unwraps TLS.
#
# If you plan to use a reverse proxy, please see
# https://matrix-org.github.io/synapse/latest/reverse_proxy.html.
#
- port: 8008
tls: false
type: http
x_forwarded: true
bind_addresses: ['::1', '127.0.0.1']
resources:
- names: [client, federation]
compress: false
# example additional_resources:
#
#additional_resources:
# "/_matrix/my/custom/endpoint":
# module: my_module.CustomRequestHandler
# config: {}
# Turn on the twisted ssh manhole service on localhost on the given
# port.
#
#- port: 9000
# bind_addresses: ['::1', '127.0.0.1']
# type: manhole
# Forward extremities can build up in a room due to networking delays between
# homeservers. Once this happens in a large room, calculation of the state of
# that room can become quite expensive. To mitigate this, once the number of
# forward extremities reaches a given threshold, Synapse will send an
# org.matrix.dummy_event event, which will reduce the forward extremities
# in the room.
#
# This setting defines the threshold (i.e. number of forward extremities in the
# room) at which dummy events are sent. The default value is 10.
#
#dummy_events_threshold: 5
## Homeserver blocking ##
# How to reach the server admin, used in ResourceLimitError
#
#admin_contact: 'mailto:admin@server.com'
# Global blocking
#
#hs_disabled: false
#hs_disabled_message: 'Human readable reason for why the HS is blocked'
# Monthly Active User Blocking
#
# Used in cases where the admin or server owner wants to limit to the
# number of monthly active users.
#
# 'limit_usage_by_mau' disables/enables monthly active user blocking. When
# enabled and a limit is reached the server returns a 'ResourceLimitError'
# with error type Codes.RESOURCE_LIMIT_EXCEEDED
#
# 'max_mau_value' is the hard limit of monthly active users above which
# the server will start blocking user actions.
#
# 'mau_trial_days' is a means to add a grace period for active users. It
# means that users must be active for this number of days before they
# can be considered active and guards against the case where lots of users
# sign up in a short space of time never to return after their initial
# session.
#
# 'mau_limit_alerting' is a means of limiting client side alerting
# should the mau limit be reached. This is useful for small instances
# where the admin has 5 mau seats (say) for 5 specific people and no
# interest increasing the mau limit further. Defaults to True, which
# means that alerting is enabled
#
#limit_usage_by_mau: false
#max_mau_value: 50
#mau_trial_days: 2
#mau_limit_alerting: false
# If enabled, the metrics for the number of monthly active users will
# be populated, however no one will be limited. If limit_usage_by_mau
# is true, this is implied to be true.
#
#mau_stats_only: false
# Sometimes the server admin will want to ensure certain accounts are
# never blocked by mau checking. These accounts are specified here.
#
#mau_limit_reserved_threepids:
# - medium: 'email'
# address: 'reserved_user@example.com'
# Used by phonehome stats to group together related servers.
#server_context: context
# Resource-constrained homeserver settings
#
# When this is enabled, the room "complexity" will be checked before a user
# joins a new remote room. If it is above the complexity limit, the server will
# disallow joining, or will instantly leave.
#
# Room complexity is an arbitrary measure based on factors such as the number of
# users in the room.
#
limit_remote_rooms:
# Uncomment to enable room complexity checking.
#
#enabled: true
# the limit above which rooms cannot be joined. The default is 1.0.
#
#complexity: 0.5
# override the error which is returned when the room is too complex.
#
#complexity_error: "This room is too complex."
# allow server admins to join complex rooms. Default is false.
#
#admins_can_join: true
# Whether to require a user to be in the room to add an alias to it.
# Defaults to 'true'.
#
#require_membership_for_aliases: false
# Whether to allow per-room membership profiles through the send of membership
# events with profile information that differ from the target's global profile.
# Defaults to 'true'.
#
#allow_per_room_profiles: false
# How long to keep redacted events in unredacted form in the database. After
# this period redacted events get replaced with their redacted form in the DB.
#
# Defaults to `7d`. Set to `null` to disable.
#
#redaction_retention_period: 28d
# How long to track users' last seen time and IPs in the database.
#
# Defaults to `28d`. Set to `null` to disable clearing out of old rows.
#
#user_ips_max_age: 14d
# Message retention policy at the server level.
#
# Room admins and mods can define a retention period for their rooms using the
# 'm.room.retention' state event, and server admins can cap this period by setting
# the 'allowed_lifetime_min' and 'allowed_lifetime_max' config options.
#
# If this feature is enabled, Synapse will regularly look for and purge events
# which are older than the room's maximum retention period. Synapse will also
# filter events received over federation so that events that should have been
# purged are ignored and not stored again.
#
retention:
# The message retention policies feature is disabled by default. Uncomment the
# following line to enable it.
#
#enabled: true
# Default retention policy. If set, Synapse will apply it to rooms that lack the
# 'm.room.retention' state event. Currently, the value of 'min_lifetime' doesn't
# matter much because Synapse doesn't take it into account yet.
#
#default_policy:
# min_lifetime: 1d
# max_lifetime: 1y
# Retention policy limits. If set, and the state of a room contains a
# 'm.room.retention' event in its state which contains a 'min_lifetime' or a
# 'max_lifetime' that's out of these bounds, Synapse will cap the room's policy
# to these limits when running purge jobs.
#
#allowed_lifetime_min: 1d
#allowed_lifetime_max: 1y
# Server admins can define the settings of the background jobs purging the
# events which lifetime has expired under the 'purge_jobs' section.
#
# If no configuration is provided, a single job will be set up to delete expired
# events in every room daily.
#
# Each job's configuration defines which range of message lifetimes the job
# takes care of. For example, if 'shortest_max_lifetime' is '2d' and
# 'longest_max_lifetime' is '3d', the job will handle purging expired events in
# rooms whose state defines a 'max_lifetime' that's both higher than 2 days, and
# lower than or equal to 3 days. Both the minimum and the maximum value of a
# range are optional, e.g. a job with no 'shortest_max_lifetime' and a
# 'longest_max_lifetime' of '3d' will handle every room with a retention policy
# which 'max_lifetime' is lower than or equal to three days.
#
# The rationale for this per-job configuration is that some rooms might have a
# retention policy with a low 'max_lifetime', where history needs to be purged
# of outdated messages on a more frequent basis than for the rest of the rooms
# (e.g. every 12h), but not want that purge to be performed by a job that's
# iterating over every room it knows, which could be heavy on the server.
#
# If any purge job is configured, it is strongly recommended to have at least
# a single job with neither 'shortest_max_lifetime' nor 'longest_max_lifetime'
# set, or one job without 'shortest_max_lifetime' and one job without
# 'longest_max_lifetime' set. Otherwise some rooms might be ignored, even if
# 'allowed_lifetime_min' and 'allowed_lifetime_max' are set, because capping a
# room's policy to these values is done after the policies are retrieved from
# Synapse's database (which is done using the range specified in a purge job's
# configuration).
#
#purge_jobs:
# - longest_max_lifetime: 3d
# interval: 12h
# - shortest_max_lifetime: 3d
# interval: 1d
# Inhibits the /requestToken endpoints from returning an error that might leak
# information about whether an e-mail address is in use or not on this
# homeserver.
# Note that for some endpoints the error situation is the e-mail already being
# used, and for others the error is entering the e-mail being unused.
# If this option is enabled, instead of returning an error, these endpoints will
# act as if no error happened and return a fake session ID ('sid') to clients.
#
#request_token_inhibit_3pid_errors: true
# A list of domains that the domain portion of 'next_link' parameters
# must match.
#
# This parameter is optionally provided by clients while requesting
# validation of an email or phone number, and maps to a link that
# users will be automatically redirected to after validation
# succeeds. Clients can make use this parameter to aid the validation
# process.
#
# The whitelist is applied whether the homeserver or an
# identity server is handling validation.
#
# The default value is no whitelist functionality; all domains are
# allowed. Setting this value to an empty list will instead disallow
# all domains.
#
#next_link_domain_whitelist: ["matrix.org"]
## TLS ##
# PEM-encoded X509 certificate for TLS.
# This certificate, as of Synapse 1.0, will need to be a valid and verifiable
# certificate, signed by a recognised Certificate Authority.
#
# Be sure to use a `.pem` file that includes the full certificate chain including
# any intermediate certificates (for instance, if using certbot, use
# `fullchain.pem` as your certificate, not `cert.pem`).
#
#tls_certificate_path: "CONFDIR/SERVERNAME.tls.crt"
# PEM-encoded private key for TLS
#
#tls_private_key_path: "CONFDIR/SERVERNAME.tls.key"
# Whether to verify TLS server certificates for outbound federation requests.
#
# Defaults to `true`. To disable certificate verification, uncomment the
# following line.
#
#federation_verify_certificates: false
# The minimum TLS version that will be used for outbound federation requests.
#
# Defaults to `1`. Configurable to `1`, `1.1`, `1.2`, or `1.3`. Note
# that setting this value higher than `1.2` will prevent federation to most
# of the public Matrix network: only configure it to `1.3` if you have an
# entirely private federation setup and you can ensure TLS 1.3 support.
#
#federation_client_minimum_tls_version: 1.2
# Skip federation certificate verification on the following whitelist
# of domains.
#
# This setting should only be used in very specific cases, such as
# federation over Tor hidden services and similar. For private networks
# of homeservers, you likely want to use a private CA instead.
#
# Only effective if federation_verify_certicates is `true`.
#
#federation_certificate_verification_whitelist:
# - lon.example.com
# - *.domain.com
# - *.onion
# List of custom certificate authorities for federation traffic.
#
# This setting should only normally be used within a private network of
# homeservers.
#
# Note that this list will replace those that are provided by your
# operating environment. Certificates must be in PEM format.
#
#federation_custom_ca_list:
# - myCA1.pem
# - myCA2.pem
# - myCA3.pem
## Federation ##
# Restrict federation to the following whitelist of domains.
# N.B. we recommend also firewalling your federation listener to limit
# inbound federation traffic as early as possible, rather than relying
# purely on this application-layer restriction. If not specified, the
# default is to whitelist everything.
#
#federation_domain_whitelist:
# - lon.example.com
# - nyc.example.com
# - syd.example.com
# Report prometheus metrics on the age of PDUs being sent to and received from
# the following domains. This can be used to give an idea of "delay" on inbound
# and outbound federation, though be aware that any delay can be due to problems
# at either end or with the intermediate network.
#
# By default, no domains are monitored in this way.
#
#federation_metrics_domains:
# - matrix.org
# - example.com
# Uncomment to disable profile lookup over federation. By default, the
# Federation API allows other homeservers to obtain profile data of any user
# on this homeserver. Defaults to 'true'.
#
#allow_profile_lookup_over_federation: false
# Uncomment to disable device display name lookup over federation. By default, the
# Federation API allows other homeservers to obtain device display names of any user
# on this homeserver. Defaults to 'true'.
#
#allow_device_name_lookup_over_federation: false
## Caching ##
# Caching can be configured through the following options.
#
# A cache 'factor' is a multiplier that can be applied to each of
# Synapse's caches in order to increase or decrease the maximum
# number of entries that can be stored.
# The number of events to cache in memory. Not affected by
# caches.global_factor.
#
#event_cache_size: 10K
caches:
# Controls the global cache factor, which is the default cache factor
# for all caches if a specific factor for that cache is not otherwise
# set.
#
# This can also be set by the "SYNAPSE_CACHE_FACTOR" environment
# variable. Setting by environment variable takes priority over
# setting through the config file.
#
# Defaults to 0.5, which will half the size of all caches.
#
#global_factor: 1.0
# A dictionary of cache name to cache factor for that individual
# cache. Overrides the global cache factor for a given cache.
#
# These can also be set through environment variables comprised
# of "SYNAPSE_CACHE_FACTOR_" + the name of the cache in capital
# letters and underscores. Setting by environment variable
# takes priority over setting through the config file.
# Ex. SYNAPSE_CACHE_FACTOR_GET_USERS_WHO_SHARE_ROOM_WITH_USER=2.0
#
# Some caches have '*' and other characters that are not
# alphanumeric or underscores. These caches can be named with or
# without the special characters stripped. For example, to specify
# the cache factor for `*stateGroupCache*` via an environment
# variable would be `SYNAPSE_CACHE_FACTOR_STATEGROUPCACHE=2.0`.
#
per_cache_factors:
#get_users_who_share_room_with_user: 2.0
# Controls how long an entry can be in a cache without having been
# accessed before being evicted. Defaults to None, which means
# entries are never evicted based on time.
#
#expiry_time: 30m
## Database ##
# The 'database' setting defines the database that synapse uses to store all of
# its data.
#
# 'name' gives the database engine to use: either 'sqlite3' (for SQLite) or
# 'psycopg2' (for PostgreSQL).
#
# 'args' gives options which are passed through to the database engine,
# except for options starting 'cp_', which are used to configure the Twisted
# connection pool. For a reference to valid arguments, see:
# * for sqlite: https://docs.python.org/3/library/sqlite3.html#sqlite3.connect
# * for postgres: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
# * for the connection pool: https://twistedmatrix.com/documents/current/api/twisted.enterprise.adbapi.ConnectionPool.html#__init__
#
#
# Example SQLite configuration:
#
#database:
# name: sqlite3
# args:
# database: /path/to/homeserver.db
#
#
# Example Postgres configuration:
#
#database:
# name: psycopg2
# args:
# user: synapse_user
# password: secretpassword
# database: synapse
# host: localhost
# port: 5432
# cp_min: 5
# cp_max: 10
#
# For more information on using Synapse with Postgres,
# see https://matrix-org.github.io/synapse/latest/postgres.html.
#
database:
name: sqlite3
args:
database: DATADIR/homeserver.db
## Logging ##
# A yaml python logging config file as described by
# https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema
#
log_config: "CONFDIR/SERVERNAME.log.config"
## Ratelimiting ##
# Ratelimiting settings for client actions (registration, login, messaging).
#
# Each ratelimiting configuration is made of two parameters:
# - per_second: number of requests a client can send per second.
# - burst_count: number of requests a client can send before being throttled.
#
# Synapse currently uses the following configurations:
# - one for messages that ratelimits sending based on the account the client
# is using
# - one for registration that ratelimits registration requests based on the
# client's IP address.
# - one for login that ratelimits login requests based on the client's IP
# address.
# - one for login that ratelimits login requests based on the account the
# client is attempting to log into.
# - one for login that ratelimits login requests based on the account the
# client is attempting to log into, based on the amount of failed login
# attempts for this account.
# - one for ratelimiting redactions by room admins. If this is not explicitly
# set then it uses the same ratelimiting as per rc_message. This is useful
# to allow room admins to deal with abuse quickly.
# - two for ratelimiting number of rooms a user can join, "local" for when
# users are joining rooms the server is already in (this is cheap) vs
# "remote" for when users are trying to join rooms not on the server (which
# can be more expensive)
# - one for ratelimiting how often a user or IP can attempt to validate a 3PID.
# - two for ratelimiting how often invites can be sent in a room or to a
# specific user.
#
# The defaults are as shown below.
#
#rc_message:
# per_second: 0.2
# burst_count: 10
#
#rc_registration:
# per_second: 0.17
# burst_count: 3
#
#rc_login:
# address:
# per_second: 0.17
# burst_count: 3
# account:
# per_second: 0.17
# burst_count: 3
# failed_attempts:
# per_second: 0.17
# burst_count: 3
#
#rc_admin_redaction:
# per_second: 1
# burst_count: 50
#
#rc_joins:
# local:
# per_second: 0.1
# burst_count: 10
# remote:
# per_second: 0.01
# burst_count: 10
#
#rc_3pid_validation:
# per_second: 0.003
# burst_count: 5
#
#rc_invites:
# per_room:
# per_second: 0.3
# burst_count: 10
# per_user:
# per_second: 0.003
# burst_count: 5
# Ratelimiting settings for incoming federation
#
# The rc_federation configuration is made up of the following settings:
# - window_size: window size in milliseconds
# - sleep_limit: number of federation requests from a single server in
# a window before the server will delay processing the request.
# - sleep_delay: duration in milliseconds to delay processing events
# from remote servers by if they go over the sleep limit.
# - reject_limit: maximum number of concurrent federation requests
# allowed from a single server
# - concurrent: number of federation requests to concurrently process
# from a single server
#
# The defaults are as shown below.
#
#rc_federation:
# window_size: 1000
# sleep_limit: 10
# sleep_delay: 500
# reject_limit: 50
# concurrent: 3
# Target outgoing federation transaction frequency for sending read-receipts,
# per-room.
#
# If we end up trying to send out more read-receipts, they will get buffered up
# into fewer transactions.
#
#federation_rr_transactions_per_room_per_second: 50
## Media Store ##
# Enable the media store service in the Synapse master. Uncomment the
# following if you are using a separate media store worker.
#
#enable_media_repo: false
# Directory where uploaded images and attachments are stored.
#
media_store_path: "DATADIR/media_store"
# Media storage providers allow media to be stored in different
# locations.
#
#media_storage_providers:
# - module: file_system
# # Whether to store newly uploaded local files
# store_local: false
# # Whether to store newly downloaded remote files
# store_remote: false
# # Whether to wait for successful storage for local uploads
# store_synchronous: false
# config:
# directory: /mnt/some/other/directory
# The largest allowed upload size in bytes
#
# If you are using a reverse proxy you may also need to set this value in
# your reverse proxy's config. Notably Nginx has a small max body size by default.
# See https://matrix-org.github.io/synapse/latest/reverse_proxy.html.
#
#max_upload_size: 50M
# Maximum number of pixels that will be thumbnailed
#
#max_image_pixels: 32M
# Whether to generate new thumbnails on the fly to precisely match
# the resolution requested by the client. If true then whenever
# a new resolution is requested by the client the server will
# generate a new thumbnail. If false the server will pick a thumbnail
# from a precalculated list.
#
#dynamic_thumbnails: false
# List of thumbnails to precalculate when an image is uploaded.
#
#thumbnail_sizes:
# - width: 32
# height: 32
# method: crop
# - width: 96
# height: 96
# method: crop
# - width: 320
# height: 240
# method: scale
# - width: 640
# height: 480
# method: scale
# - width: 800
# height: 600
# method: scale
# Is the preview URL API enabled?
#
# 'false' by default: uncomment the following to enable it (and specify a
# url_preview_ip_range_blacklist blacklist).
#
#url_preview_enabled: true
# List of IP address CIDR ranges that the URL preview spider is denied
# from accessing. There are no defaults: you must explicitly
# specify a list for URL previewing to work. You should specify any
# internal services in your network that you do not want synapse to try
# to connect to, otherwise anyone in any Matrix room could cause your
# synapse to issue arbitrary GET requests to your internal services,
# causing serious security issues.
#
# (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly
# listed here, since they correspond to unroutable addresses.)
#
# This must be specified if url_preview_enabled is set. It is recommended that
# you uncomment the following list as a starting point.
#
#url_preview_ip_range_blacklist:
# - '127.0.0.0/8'
# - '10.0.0.0/8'
# - '172.16.0.0/12'
# - '192.168.0.0/16'
# - '100.64.0.0/10'
# - '192.0.0.0/24'
# - '169.254.0.0/16'
# - '192.88.99.0/24'
# - '198.18.0.0/15'
# - '192.0.2.0/24'
# - '198.51.100.0/24'
# - '203.0.113.0/24'
# - '224.0.0.0/4'
# - '::1/128'
# - 'fe80::/10'
# - 'fc00::/7'
# - '2001:db8::/32'
# - 'ff00::/8'
# - 'fec0::/10'
# List of IP address CIDR ranges that the URL preview spider is allowed
# to access even if they are specified in url_preview_ip_range_blacklist.
# This is useful for specifying exceptions to wide-ranging blacklisted
# target IP ranges - e.g. for enabling URL previews for a specific private
# website only visible in your network.
#
#url_preview_ip_range_whitelist:
# - '192.168.1.1'
# Optional list of URL matches that the URL preview spider is
# denied from accessing. You should use url_preview_ip_range_blacklist
# in preference to this, otherwise someone could define a public DNS
# entry that points to a private IP address and circumvent the blacklist.
# This is more useful if you know there is an entire shape of URL that
# you know that will never want synapse to try to spider.
#
# Each list entry is a dictionary of url component attributes as returned
# by urlparse.urlsplit as applied to the absolute form of the URL. See
# https://docs.python.org/2/library/urlparse.html#urlparse.urlsplit
# The values of the dictionary are treated as an filename match pattern
# applied to that component of URLs, unless they start with a ^ in which
# case they are treated as a regular expression match. If all the
# specified component matches for a given list item succeed, the URL is
# blacklisted.
#
#url_preview_url_blacklist:
# # blacklist any URL with a username in its URI
# - username: '*'
#
# # blacklist all *.google.com URLs
# - netloc: 'google.com'
# - netloc: '*.google.com'
#
# # blacklist all plain HTTP URLs
# - scheme: 'http'
#
# # blacklist http(s)://www.acme.com/foo
# - netloc: 'www.acme.com'
# path: '/foo'
#
# # blacklist any URL with a literal IPv4 address
# - netloc: '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'
# The largest allowed URL preview spidering size in bytes
#
#max_spider_size: 10M
# A list of values for the Accept-Language HTTP header used when
# downloading webpages during URL preview generation. This allows
# Synapse to specify the preferred languages that URL previews should
# be in when communicating with remote servers.
#
# Each value is a IETF language tag; a 2-3 letter identifier for a
# language, optionally followed by subtags separated by '-', specifying
# a country or region variant.
#
# Multiple values can be provided, and a weight can be added to each by
# using quality value syntax (;q=). '*' translates to any language.
#
# Defaults to "en".
#
# Example:
#
# url_preview_accept_language:
# - en-UK
# - en-US;q=0.9
# - fr;q=0.8
# - *;q=0.7
#
url_preview_accept_language:
# - en
## Captcha ##
# See docs/CAPTCHA_SETUP.md for full details of configuring this.
# This homeserver's ReCAPTCHA public key. Must be specified if
# enable_registration_captcha is enabled.
#
#recaptcha_public_key: "YOUR_PUBLIC_KEY"
# This homeserver's ReCAPTCHA private key. Must be specified if
# enable_registration_captcha is enabled.
#
#recaptcha_private_key: "YOUR_PRIVATE_KEY"
# Uncomment to enable ReCaptcha checks when registering, preventing signup
# unless a captcha is answered. Requires a valid ReCaptcha
# public/private key. Defaults to 'false'.
#
#enable_registration_captcha: true
# The API endpoint to use for verifying m.login.recaptcha responses.
# Defaults to "https://www.recaptcha.net/recaptcha/api/siteverify".
#
#recaptcha_siteverify_api: "https://my.recaptcha.site"
## TURN ##
# The public URIs of the TURN server to give to clients
#
#turn_uris: []
# The shared secret used to compute passwords for the TURN server
#
#turn_shared_secret: "YOUR_SHARED_SECRET"
# The Username and password if the TURN server needs them and
# does not use a token
#
#turn_username: "TURNSERVER_USERNAME"
#turn_password: "TURNSERVER_PASSWORD"
# How long generated TURN credentials last
#
#turn_user_lifetime: 1h
# Whether guests should be allowed to use the TURN server.
# This defaults to True, otherwise VoIP will be unreliable for guests.
# However, it does introduce a slight security risk as it allows users to
# connect to arbitrary endpoints without having first signed up for a
# valid account (e.g. by passing a CAPTCHA).
#
#turn_allow_guests: true
## Registration ##
#
# Registration can be rate-limited using the parameters in the "Ratelimiting"
# section of this file.
# Enable registration for new users.
#
#enable_registration: false
# Time that a user's session remains valid for, after they log in.
#
# Note that this is not currently compatible with guest logins.
#
# Note also that this is calculated at login time: changes are not applied
# retrospectively to users who have already logged in.
#
# By default, this is infinite.
#
#session_lifetime: 24h
# The user must provide all of the below types of 3PID when registering.
#
#registrations_require_3pid:
# - email
# - msisdn
# Explicitly disable asking for MSISDNs from the registration
# flow (overrides registrations_require_3pid if MSISDNs are set as required)
#
#disable_msisdn_registration: true
# Mandate that users are only allowed to associate certain formats of
# 3PIDs with accounts on this server.
#
#allowed_local_3pids:
# - medium: email
# pattern: '^[^@]+@matrix\.org$'
# - medium: email
# pattern: '^[^@]+@vector\.im$'
# - medium: msisdn
# pattern: '\+44'
# Enable 3PIDs lookup requests to identity servers from this server.
#
#enable_3pid_lookup: true
# If set, allows registration of standard or admin accounts by anyone who
# has the shared secret, even if registration is otherwise disabled.
#
#registration_shared_secret: <PRIVATE STRING>
# Set the number of bcrypt rounds used to generate password hash.
# Larger numbers increase the work factor needed to generate the hash.
# The default number is 12 (which equates to 2^12 rounds).
# N.B. that increasing this will exponentially increase the time required
# to register or login - e.g. 24 => 2^24 rounds which will take >20 mins.
#
#bcrypt_rounds: 12
# Allows users to register as guests without a password/email/etc, and
# participate in rooms hosted on this server which have been made
# accessible to anonymous users.
#
#allow_guest_access: false
# The identity server which we suggest that clients should use when users log
# in on this server.
#
# (By default, no suggestion is made, so it is left up to the client.
# This setting is ignored unless public_baseurl is also set.)
#
#default_identity_server: https://matrix.org
# Handle threepid (email/phone etc) registration and password resets through a set of
# *trusted* identity servers. Note that this allows the configured identity server to
# reset passwords for accounts!
#
# Be aware that if `email` is not set, and SMTP options have not been
# configured in the email config block, registration and user password resets via
# email will be globally disabled.
#
# Additionally, if `msisdn` is not set, registration and password resets via msisdn
# will be disabled regardless, and users will not be able to associate an msisdn
# identifier to their account. This is due to Synapse currently not supporting
# any method of sending SMS messages on its own.
#
# To enable using an identity server for operations regarding a particular third-party
# identifier type, set the value to the URL of that identity server as shown in the
# examples below.
#
# Servers handling the these requests must answer the `/requestToken` endpoints defined
# by the Matrix Identity Service API specification:
# https://matrix.org/docs/spec/identity_service/latest
#
# If a delegate is specified, the config option public_baseurl must also be filled out.
#
account_threepid_delegates:
#email: https://example.com # Delegate email sending to example.com
#msisdn: http://localhost:8090 # Delegate SMS sending to this local process
# Whether users are allowed to change their displayname after it has
# been initially set. Useful when provisioning users based on the
# contents of a third-party directory.
#
# Does not apply to server administrators. Defaults to 'true'
#
#enable_set_displayname: false
# Whether users are allowed to change their avatar after it has been
# initially set. Useful when provisioning users based on the contents
# of a third-party directory.
#
# Does not apply to server administrators. Defaults to 'true'
#
#enable_set_avatar_url: false
# Whether users can change the 3PIDs associated with their accounts
# (email address and msisdn).
#
# Defaults to 'true'
#
#enable_3pid_changes: false
# Users who register on this homeserver will automatically be joined
# to these rooms.
#
# By default, any room aliases included in this list will be created
# as a publicly joinable room when the first user registers for the
# homeserver. This behaviour can be customised with the settings below.
# If the room already exists, make certain it is a publicly joinable
# room. The join rule of the room must be set to 'public'.
#
#auto_join_rooms:
# - "#example:example.com"
# Where auto_join_rooms are specified, setting this flag ensures that the
# the rooms exist by creating them when the first user on the
# homeserver registers.
#
# By default the auto-created rooms are publicly joinable from any federated
# server. Use the autocreate_auto_join_rooms_federated and
# autocreate_auto_join_room_preset settings below to customise this behaviour.
#
# Setting to false means that if the rooms are not manually created,
# users cannot be auto-joined since they do not exist.
#
# Defaults to true. Uncomment the following line to disable automatically
# creating auto-join rooms.
#
#autocreate_auto_join_rooms: false
# Whether the auto_join_rooms that are auto-created are available via
# federation. Only has an effect if autocreate_auto_join_rooms is true.
#
# Note that whether a room is federated cannot be modified after
# creation.
#
# Defaults to true: the room will be joinable from other servers.
# Uncomment the following to prevent users from other homeservers from
# joining these rooms.
#
#autocreate_auto_join_rooms_federated: false
# The room preset to use when auto-creating one of auto_join_rooms. Only has an
# effect if autocreate_auto_join_rooms is true.
#
# This can be one of "public_chat", "private_chat", or "trusted_private_chat".
# If a value of "private_chat" or "trusted_private_chat" is used then
# auto_join_mxid_localpart must also be configured.
#
# Defaults to "public_chat", meaning that the room is joinable by anyone, including
# federated servers if autocreate_auto_join_rooms_federated is true (the default).
# Uncomment the following to require an invitation to join these rooms.
#
#autocreate_auto_join_room_preset: private_chat
# The local part of the user id which is used to create auto_join_rooms if
# autocreate_auto_join_rooms is true. If this is not provided then the
# initial user account that registers will be used to create the rooms.
#
# The user id is also used to invite new users to any auto-join rooms which
# are set to invite-only.
#
# It *must* be configured if autocreate_auto_join_room_preset is set to
# "private_chat" or "trusted_private_chat".
#
# Note that this must be specified in order for new users to be correctly
# invited to any auto-join rooms which have been set to invite-only (either
# at the time of creation or subsequently).
#
# Note that, if the room already exists, this user must be joined and
# have the appropriate permissions to invite new members.
#
#auto_join_mxid_localpart: system
# When auto_join_rooms is specified, setting this flag to false prevents
# guest accounts from being automatically joined to the rooms.
#
# Defaults to true.
#
#auto_join_rooms_for_guests: false
## Metrics ###
# Enable collection and rendering of performance metrics
#
#enable_metrics: false
# Enable sentry integration
# NOTE: While attempts are made to ensure that the logs don't contain
# any sensitive information, this cannot be guaranteed. By enabling
# this option the sentry server may therefore receive sensitive
# information, and it in turn may then diseminate sensitive information
# through insecure notification channels if so configured.
#
#sentry:
# dsn: "..."
# Flags to enable Prometheus metrics which are not suitable to be
# enabled by default, either for performance reasons or limited use.
#
metrics_flags:
# Publish synapse_federation_known_servers, a gauge of the number of
# servers this homeserver knows about, including itself. May cause
# performance problems on large homeservers.
#
#known_servers: true
# Whether or not to report anonymized homeserver usage statistics.
#
#report_stats: true|false
# The endpoint to report the anonymized homeserver usage statistics to.
# Defaults to https://matrix.org/report-usage-stats/push
#
#report_stats_endpoint: https://example.com/report-usage-stats/push
## API Configuration ##
# Controls for the state that is shared with users who receive an invite
# to a room
#
room_prejoin_state:
# By default, the following state event types are shared with users who
# receive invites to the room:
#
# - m.room.join_rules
# - m.room.canonical_alias
# - m.room.avatar
# - m.room.encryption
# - m.room.name
# - m.room.create
#
# Uncomment the following to disable these defaults (so that only the event
# types listed in 'additional_event_types' are shared). Defaults to 'false'.
#
#disable_default_event_types: true
# Additional state event types to share with users when they are invited
# to a room.
#
# By default, this list is empty (so only the default event types are shared).
#
#additional_event_types:
# - org.example.custom.event.type
# A list of application service config files to use
#
#app_service_config_files:
# - app_service_1.yaml
# - app_service_2.yaml
# Uncomment to enable tracking of application service IP addresses. Implicitly
# enables MAU tracking for application service users.
#
#track_appservice_user_ips: true
# a secret which is used to sign access tokens. If none is specified,
# the registration_shared_secret is used, if one is given; otherwise,
# a secret key is derived from the signing key.
#
#macaroon_secret_key: <PRIVATE STRING>
# a secret which is used to calculate HMACs for form values, to stop
# falsification of values. Must be specified for the User Consent
# forms to work.
#
#form_secret: <PRIVATE STRING>
## Signing Keys ##
# Path to the signing key to sign messages with
#
signing_key_path: "CONFDIR/SERVERNAME.signing.key"
# The keys that the server used to sign messages with but won't use
# to sign new messages.
#
old_signing_keys:
# For each key, `key` should be the base64-encoded public key, and
# `expired_ts`should be the time (in milliseconds since the unix epoch) that
# it was last used.
#
# It is possible to build an entry from an old signing.key file using the
# `export_signing_key` script which is provided with synapse.
#
# For example:
#
#"ed25519:id": { key: "base64string", expired_ts: 123456789123 }
# How long key response published by this server is valid for.
# Used to set the valid_until_ts in /key/v2 APIs.
# Determines how quickly servers will query to check which keys
# are still valid.
#
#key_refresh_interval: 1d
# The trusted servers to download signing keys from.
#
# When we need to fetch a signing key, each server is tried in parallel.
#
# Normally, the connection to the key server is validated via TLS certificates.
# Additional security can be provided by configuring a `verify key`, which
# will make synapse check that the response is signed by that key.
#
# This setting supercedes an older setting named `perspectives`. The old format
# is still supported for backwards-compatibility, but it is deprecated.
#
# 'trusted_key_servers' defaults to matrix.org, but using it will generate a
# warning on start-up. To suppress this warning, set
# 'suppress_key_server_warning' to true.
#
# Options for each entry in the list include:
#
# server_name: the name of the server. required.
#
# verify_keys: an optional map from key id to base64-encoded public key.
# If specified, we will check that the response is signed by at least
# one of the given keys.
#
# accept_keys_insecurely: a boolean. Normally, if `verify_keys` is unset,
# and federation_verify_certificates is not `true`, synapse will refuse
# to start, because this would allow anyone who can spoof DNS responses
# to masquerade as the trusted key server. If you know what you are doing
# and are sure that your network environment provides a secure connection
# to the key server, you can set this to `true` to override this
# behaviour.
#
# An example configuration might look like:
#
#trusted_key_servers:
# - server_name: "my_trusted_server.example.com"
# verify_keys:
# "ed25519:auto": "abcdefghijklmnopqrstuvwxyzabcdefghijklmopqr"
# - server_name: "my_other_trusted_server.example.com"
#
trusted_key_servers:
- server_name: "matrix.org"
# Uncomment the following to disable the warning that is emitted when the
# trusted_key_servers include 'matrix.org'. See above.
#
#suppress_key_server_warning: true
# The signing keys to use when acting as a trusted key server. If not specified
# defaults to the server signing key.
#
# Can contain multiple keys, one per line.
#
#key_server_signing_keys_path: "key_server_signing_keys.key"
## Single sign-on integration ##
# The following settings can be used to make Synapse use a single sign-on
# provider for authentication, instead of its internal password database.
#
# You will probably also want to set the following options to `false` to
# disable the regular login/registration flows:
# * enable_registration
# * password_config.enabled
#
# You will also want to investigate the settings under the "sso" configuration
# section below.
# Enable SAML2 for registration and login. Uses pysaml2.
#
# At least one of `sp_config` or `config_path` must be set in this section to
# enable SAML login.
#
# Once SAML support is enabled, a metadata file will be exposed at
# https://<server>:<port>/_synapse/client/saml2/metadata.xml, which you may be able to
# use to configure your SAML IdP with. Alternatively, you can manually configure
# the IdP to use an ACS location of
# https://<server>:<port>/_synapse/client/saml2/authn_response.
#
saml2_config:
# `sp_config` is the configuration for the pysaml2 Service Provider.
# See pysaml2 docs for format of config.
#
# Default values will be used for the 'entityid' and 'service' settings,
# so it is not normally necessary to specify them unless you need to
# override them.
#
sp_config:
# Point this to the IdP's metadata. You must provide either a local
# file via the `local` attribute or (preferably) a URL via the
# `remote` attribute.
#
#metadata:
# local: ["saml2/idp.xml"]
# remote:
# - url: https://our_idp/metadata.xml
# Allowed clock difference in seconds between the homeserver and IdP.
#
# Uncomment the below to increase the accepted time difference from 0 to 3 seconds.
#
#accepted_time_diff: 3
# By default, the user has to go to our login page first. If you'd like
# to allow IdP-initiated login, set 'allow_unsolicited: true' in a
# 'service.sp' section:
#
#service:
# sp:
# allow_unsolicited: true
# The examples below are just used to generate our metadata xml, and you
# may well not need them, depending on your setup. Alternatively you
# may need a whole lot more detail - see the pysaml2 docs!
#description: ["My awesome SP", "en"]
#name: ["Test SP", "en"]
#ui_info:
# display_name:
# - lang: en
# text: "Display Name is the descriptive name of your service."
# description:
# - lang: en
# text: "Description should be a short paragraph explaining the purpose of the service."
# information_url:
# - lang: en
# text: "https://example.com/terms-of-service"
# privacy_statement_url:
# - lang: en
# text: "https://example.com/privacy-policy"
# keywords:
# - lang: en
# text: ["Matrix", "Element"]
# logo:
# - lang: en
# text: "https://example.com/logo.svg"
# width: "200"
# height: "80"
#organization:
# name: Example com
# display_name:
# - ["Example co", "en"]
# url: "http://example.com"
#contact_person:
# - given_name: Bob
# sur_name: "the Sysadmin"
# email_address": ["admin@example.com"]
# contact_type": technical
# Instead of putting the config inline as above, you can specify a
# separate pysaml2 configuration file:
#
#config_path: "CONFDIR/sp_conf.py"
# The lifetime of a SAML session. This defines how long a user has to
# complete the authentication process, if allow_unsolicited is unset.
# The default is 15 minutes.
#
#saml_session_lifetime: 5m
# An external module can be provided here as a custom solution to
# mapping attributes returned from a saml provider onto a matrix user.
#
user_mapping_provider:
# The custom module's class. Uncomment to use a custom module.
#
#module: mapping_provider.SamlMappingProvider
# Custom configuration values for the module. Below options are
# intended for the built-in provider, they should be changed if
# using a custom module. This section will be passed as a Python
# dictionary to the module's `parse_config` method.
#
config:
# The SAML attribute (after mapping via the attribute maps) to use
# to derive the Matrix ID from. 'uid' by default.
#
# Note: This used to be configured by the
# saml2_config.mxid_source_attribute option. If that is still
# defined, its value will be used instead.
#
#mxid_source_attribute: displayName
# The mapping system to use for mapping the saml attribute onto a
# matrix ID.
#
# Options include:
# * 'hexencode' (which maps unpermitted characters to '=xx')
# * 'dotreplace' (which replaces unpermitted characters with
# '.').
# The default is 'hexencode'.
#
# Note: This used to be configured by the
# saml2_config.mxid_mapping option. If that is still defined, its
# value will be used instead.
#
#mxid_mapping: dotreplace
# In previous versions of synapse, the mapping from SAML attribute to
# MXID was always calculated dynamically rather than stored in a
# table. For backwards- compatibility, we will look for user_ids
# matching such a pattern before creating a new account.
#
# This setting controls the SAML attribute which will be used for this
# backwards-compatibility lookup. Typically it should be 'uid', but if
# the attribute maps are changed, it may be necessary to change it.
#
# The default is 'uid'.
#
#grandfathered_mxid_source_attribute: upn
# It is possible to configure Synapse to only allow logins if SAML attributes
# match particular values. The requirements can be listed under
# `attribute_requirements` as shown below. All of the listed attributes must
# match for the login to be permitted.
#
#attribute_requirements:
# - attribute: userGroup
# value: "staff"
# - attribute: department
# value: "sales"
# If the metadata XML contains multiple IdP entities then the `idp_entityid`
# option must be set to the entity to redirect users to.
#
# Most deployments only have a single IdP entity and so should omit this
# option.
#
#idp_entityid: 'https://our_idp/entityid'
# List of OpenID Connect (OIDC) / OAuth 2.0 identity providers, for registration
# and login.
#
# Options for each entry include:
#
# idp_id: a unique identifier for this identity provider. Used internally
# by Synapse; should be a single word such as 'github'.
#
# Note that, if this is changed, users authenticating via that provider
# will no longer be recognised as the same user!
#
# (Use "oidc" here if you are migrating from an old "oidc_config"
# configuration.)
#
# idp_name: A user-facing name for this identity provider, which is used to
# offer the user a choice of login mechanisms.
#
# idp_icon: An optional icon for this identity provider, which is presented
# by clients and Synapse's own IdP picker page. If given, must be an
# MXC URI of the format mxc://<server-name>/<media-id>. (An easy way to
# obtain such an MXC URI is to upload an image to an (unencrypted) room
# and then copy the "url" from the source of the event.)
#
# idp_brand: An optional brand for this identity provider, allowing clients
# to style the login flow according to the identity provider in question.
# See the spec for possible options here.
#
# discover: set to 'false' to disable the use of the OIDC discovery mechanism
# to discover endpoints. Defaults to true.
#
# issuer: Required. The OIDC issuer. Used to validate tokens and (if discovery
# is enabled) to discover the provider's endpoints.
#
# client_id: Required. oauth2 client id to use.
#
# client_secret: oauth2 client secret to use. May be omitted if
# client_secret_jwt_key is given, or if client_auth_method is 'none'.
#
# client_secret_jwt_key: Alternative to client_secret: details of a key used
# to create a JSON Web Token to be used as an OAuth2 client secret. If
# given, must be a dictionary with the following properties:
#
# key: a pem-encoded signing key. Must be a suitable key for the
# algorithm specified. Required unless 'key_file' is given.
#
# key_file: the path to file containing a pem-encoded signing key file.
# Required unless 'key' is given.
#
# jwt_header: a dictionary giving properties to include in the JWT
# header. Must include the key 'alg', giving the algorithm used to
# sign the JWT, such as "ES256", using the JWA identifiers in
# RFC7518.
#
# jwt_payload: an optional dictionary giving properties to include in
# the JWT payload. Normally this should include an 'iss' key.
#
# client_auth_method: auth method to use when exchanging the token. Valid
# values are 'client_secret_basic' (default), 'client_secret_post' and
# 'none'.
#
# scopes: list of scopes to request. This should normally include the "openid"
# scope. Defaults to ["openid"].
#
# authorization_endpoint: the oauth2 authorization endpoint. Required if
# provider discovery is disabled.
#
# token_endpoint: the oauth2 token endpoint. Required if provider discovery is
# disabled.
#
# userinfo_endpoint: the OIDC userinfo endpoint. Required if discovery is
# disabled and the 'openid' scope is not requested.
#
# jwks_uri: URI where to fetch the JWKS. Required if discovery is disabled and
# the 'openid' scope is used.
#
# skip_verification: set to 'true' to skip metadata verification. Use this if
# you are connecting to a provider that is not OpenID Connect compliant.
# Defaults to false. Avoid this in production.
#
# user_profile_method: Whether to fetch the user profile from the userinfo
# endpoint. Valid values are: 'auto' or 'userinfo_endpoint'.
#
# Defaults to 'auto', which fetches the userinfo endpoint if 'openid' is
# included in 'scopes'. Set to 'userinfo_endpoint' to always fetch the
# userinfo endpoint.
#
# allow_existing_users: set to 'true' to allow a user logging in via OIDC to
# match a pre-existing account instead of failing. This could be used if
# switching from password logins to OIDC. Defaults to false.
#
# user_mapping_provider: Configuration for how attributes returned from a OIDC
# provider are mapped onto a matrix user. This setting has the following
# sub-properties:
#
# module: The class name of a custom mapping module. Default is
# 'synapse.handlers.oidc.JinjaOidcMappingProvider'.
# See https://matrix-org.github.io/synapse/latest/sso_mapping_providers.html#openid-mapping-providers
# for information on implementing a custom mapping provider.
#
# config: Configuration for the mapping provider module. This section will
# be passed as a Python dictionary to the user mapping provider
# module's `parse_config` method.
#
# For the default provider, the following settings are available:
#
# subject_claim: name of the claim containing a unique identifier
# for the user. Defaults to 'sub', which OpenID Connect
# compliant providers should provide.
#
# localpart_template: Jinja2 template for the localpart of the MXID.
# If this is not set, the user will be prompted to choose their
# own username (see 'sso_auth_account_details.html' in the 'sso'
# section of this file).
#
# display_name_template: Jinja2 template for the display name to set
# on first login. If unset, no displayname will be set.
#
# email_template: Jinja2 template for the email address of the user.
# If unset, no email address will be added to the account.
#
# extra_attributes: a map of Jinja2 templates for extra attributes
# to send back to the client during login.
# Note that these are non-standard and clients will ignore them
# without modifications.
#
# When rendering, the Jinja2 templates are given a 'user' variable,
# which is set to the claims returned by the UserInfo Endpoint and/or
# in the ID Token.
#
# It is possible to configure Synapse to only allow logins if certain attributes
# match particular values in the OIDC userinfo. The requirements can be listed under
# `attribute_requirements` as shown below. All of the listed attributes must
# match for the login to be permitted. Additional attributes can be added to
# userinfo by expanding the `scopes` section of the OIDC config to retrieve
# additional information from the OIDC provider.
#
# If the OIDC claim is a list, then the attribute must match any value in the list.
# Otherwise, it must exactly match the value of the claim. Using the example
# below, the `family_name` claim MUST be "Stephensson", but the `groups`
# claim MUST contain "admin".
#
# attribute_requirements:
# - attribute: family_name
# value: "Stephensson"
# - attribute: groups
# value: "admin"
#
# See https://matrix-org.github.io/synapse/latest/openid.html
# for information on how to configure these options.
#
# For backwards compatibility, it is also possible to configure a single OIDC
# provider via an 'oidc_config' setting. This is now deprecated and admins are
# advised to migrate to the 'oidc_providers' format. (When doing that migration,
# use 'oidc' for the idp_id to ensure that existing users continue to be
# recognised.)
#
oidc_providers:
# Generic example
#
#- idp_id: my_idp
# idp_name: "My OpenID provider"
# idp_icon: "mxc://example.com/mediaid"
# discover: false
# issuer: "https://accounts.example.com/"
# client_id: "provided-by-your-issuer"
# client_secret: "provided-by-your-issuer"
# client_auth_method: client_secret_post
# scopes: ["openid", "profile"]
# authorization_endpoint: "https://accounts.example.com/oauth2/auth"
# token_endpoint: "https://accounts.example.com/oauth2/token"
# userinfo_endpoint: "https://accounts.example.com/userinfo"
# jwks_uri: "https://accounts.example.com/.well-known/jwks.json"
# skip_verification: true
# user_mapping_provider:
# config:
# subject_claim: "id"
# localpart_template: "{{ user.login }}"
# display_name_template: "{{ user.name }}"
# email_template: "{{ user.email }}"
# attribute_requirements:
# - attribute: userGroup
# value: "synapseUsers"
# Enable Central Authentication Service (CAS) for registration and login.
#
cas_config:
# Uncomment the following to enable authorization against a CAS server.
# Defaults to false.
#
#enabled: true
# The URL of the CAS authorization endpoint.
#
#server_url: "https://cas-server.com"
# The attribute of the CAS response to use as the display name.
#
# If unset, no displayname will be set.
#
#displayname_attribute: name
# It is possible to configure Synapse to only allow logins if CAS attributes
# match particular values. All of the keys in the mapping below must exist
# and the values must match the given value. Alternately if the given value
# is None then any value is allowed (the attribute just must exist).
# All of the listed attributes must match for the login to be permitted.
#
#required_attributes:
# userGroup: "staff"
# department: None
# Additional settings to use with single-sign on systems such as OpenID Connect,
# SAML2 and CAS.
#
sso:
# A list of client URLs which are whitelisted so that the user does not
# have to confirm giving access to their account to the URL. Any client
# whose URL starts with an entry in the following list will not be subject
# to an additional confirmation step after the SSO login is completed.
#
# WARNING: An entry such as "https://my.client" is insecure, because it
# will also match "https://my.client.evil.site", exposing your users to
# phishing attacks from evil.site. To avoid this, include a slash after the
# hostname: "https://my.client/".
#
# If public_baseurl is set, then the login fallback page (used by clients
# that don't natively support the required login flows) is whitelisted in
# addition to any URLs in this list.
#
# By default, this list is empty.
#
#client_whitelist:
# - https://riot.im/develop
# - https://my.custom.client/
# Uncomment to keep a user's profile fields in sync with information from
# the identity provider. Currently only syncing the displayname is
# supported. Fields are checked on every SSO login, and are updated
# if necessary.
#
# Note that enabling this option will override user profile information,
# regardless of whether users have opted-out of syncing that
# information when first signing in. Defaults to false.
#
#update_profile_information: true
# Directory in which Synapse will try to find the template files below.
# If not set, or the files named below are not found within the template
# directory, default templates from within the Synapse package will be used.
#
# Synapse will look for the following templates in this directory:
#
# * HTML page to prompt the user to choose an Identity Provider during
# login: 'sso_login_idp_picker.html'.
#
# This is only used if multiple SSO Identity Providers are configured.
#
# When rendering, this template is given the following variables:
# * redirect_url: the URL that the user will be redirected to after
# login.
#
# * server_name: the homeserver's name.
#
# * providers: a list of available Identity Providers. Each element is
# an object with the following attributes:
#
# * idp_id: unique identifier for the IdP
# * idp_name: user-facing name for the IdP
# * idp_icon: if specified in the IdP config, an MXC URI for an icon
# for the IdP
# * idp_brand: if specified in the IdP config, a textual identifier
# for the brand of the IdP
#
# The rendered HTML page should contain a form which submits its results
# back as a GET request, with the following query parameters:
#
# * redirectUrl: the client redirect URI (ie, the `redirect_url` passed
# to the template)
#
# * idp: the 'idp_id' of the chosen IDP.
#
# * HTML page to prompt new users to enter a userid and confirm other
# details: 'sso_auth_account_details.html'. This is only shown if the
# SSO implementation (with any user_mapping_provider) does not return
# a localpart.
#
# When rendering, this template is given the following variables:
#
# * server_name: the homeserver's name.
#
# * idp: details of the SSO Identity Provider that the user logged in
# with: an object with the following attributes:
#
# * idp_id: unique identifier for the IdP
# * idp_name: user-facing name for the IdP
# * idp_icon: if specified in the IdP config, an MXC URI for an icon
# for the IdP
# * idp_brand: if specified in the IdP config, a textual identifier
# for the brand of the IdP
#
# * user_attributes: an object containing details about the user that
# we received from the IdP. May have the following attributes:
#
# * display_name: the user's display_name
# * emails: a list of email addresses
#
# The template should render a form which submits the following fields:
#
# * username: the localpart of the user's chosen user id
#
# * HTML page allowing the user to consent to the server's terms and
# conditions. This is only shown for new users, and only if
# `user_consent.require_at_registration` is set.
#
# When rendering, this template is given the following variables:
#
# * server_name: the homeserver's name.
#
# * user_id: the user's matrix proposed ID.
#
# * user_profile.display_name: the user's proposed display name, if any.
#
# * consent_version: the version of the terms that the user will be
# shown
#
# * terms_url: a link to the page showing the terms.
#
# The template should render a form which submits the following fields:
#
# * accepted_version: the version of the terms accepted by the user
# (ie, 'consent_version' from the input variables).
#
# * HTML page for a confirmation step before redirecting back to the client
# with the login token: 'sso_redirect_confirm.html'.
#
# When rendering, this template is given the following variables:
#
# * redirect_url: the URL the user is about to be redirected to.
#
# * display_url: the same as `redirect_url`, but with the query
# parameters stripped. The intention is to have a
# human-readable URL to show to users, not to use it as
# the final address to redirect to.
#
# * server_name: the homeserver's name.
#
# * new_user: a boolean indicating whether this is the user's first time
# logging in.
#
# * user_id: the user's matrix ID.
#
# * user_profile.avatar_url: an MXC URI for the user's avatar, if any.
# None if the user has not set an avatar.
#
# * user_profile.display_name: the user's display name. None if the user
# has not set a display name.
#
# * HTML page which notifies the user that they are authenticating to confirm
# an operation on their account during the user interactive authentication
# process: 'sso_auth_confirm.html'.
#
# When rendering, this template is given the following variables:
# * redirect_url: the URL the user is about to be redirected to.
#
# * description: the operation which the user is being asked to confirm
#
# * idp: details of the Identity Provider that we will use to confirm
# the user's identity: an object with the following attributes:
#
# * idp_id: unique identifier for the IdP
# * idp_name: user-facing name for the IdP
# * idp_icon: if specified in the IdP config, an MXC URI for an icon
# for the IdP
# * idp_brand: if specified in the IdP config, a textual identifier
# for the brand of the IdP
#
# * HTML page shown after a successful user interactive authentication session:
# 'sso_auth_success.html'.
#
# Note that this page must include the JavaScript which notifies of a successful authentication
# (see https://matrix.org/docs/spec/client_server/r0.6.0#fallback).
#
# This template has no additional variables.
#
# * HTML page shown after a user-interactive authentication session which
# does not map correctly onto the expected user: 'sso_auth_bad_user.html'.
#
# When rendering, this template is given the following variables:
# * server_name: the homeserver's name.
# * user_id_to_verify: the MXID of the user that we are trying to
# validate.
#
# * HTML page shown during single sign-on if a deactivated user (according to Synapse's database)
# attempts to login: 'sso_account_deactivated.html'.
#
# This template has no additional variables.
#
# * HTML page to display to users if something goes wrong during the
# OpenID Connect authentication process: 'sso_error.html'.
#
# When rendering, this template is given two variables:
# * error: the technical name of the error
# * error_description: a human-readable message for the error
#
# You can see the default templates at:
# https://github.com/matrix-org/synapse/tree/master/synapse/res/templates
#
#template_dir: "res/templates"
# JSON web token integration. The following settings can be used to make
# Synapse JSON web tokens for authentication, instead of its internal
# password database.
#
# Each JSON Web Token needs to contain a "sub" (subject) claim, which is
# used as the localpart of the mxid.
#
# Additionally, the expiration time ("exp"), not before time ("nbf"),
# and issued at ("iat") claims are validated if present.
#
# Note that this is a non-standard login type and client support is
# expected to be non-existent.
#
# See https://matrix-org.github.io/synapse/latest/jwt.html.
#
#jwt_config:
# Uncomment the following to enable authorization using JSON web
# tokens. Defaults to false.
#
#enabled: true
# This is either the private shared secret or the public key used to
# decode the contents of the JSON web token.
#
# Required if 'enabled' is true.
#
#secret: "provided-by-your-issuer"
# The algorithm used to sign the JSON web token.
#
# Supported algorithms are listed at
# https://pyjwt.readthedocs.io/en/latest/algorithms.html
#
# Required if 'enabled' is true.
#
#algorithm: "provided-by-your-issuer"
# The issuer to validate the "iss" claim against.
#
# Optional, if provided the "iss" claim will be required and
# validated for all JSON web tokens.
#
#issuer: "provided-by-your-issuer"
# A list of audiences to validate the "aud" claim against.
#
# Optional, if provided the "aud" claim will be required and
# validated for all JSON web tokens.
#
# Note that if the "aud" claim is included in a JSON web token then
# validation will fail without configuring audiences.
#
#audiences:
# - "provided-by-your-issuer"
password_config:
# Uncomment to disable password login
#
#enabled: false
# Uncomment to disable authentication against the local password
# database. This is ignored if `enabled` is false, and is only useful
# if you have other password_providers.
#
#localdb_enabled: false
# Uncomment and change to a secret random string for extra security.
# DO NOT CHANGE THIS AFTER INITIAL SETUP!
#
#pepper: "EVEN_MORE_SECRET"
# Define and enforce a password policy. Each parameter is optional.
# This is an implementation of MSC2000.
#
policy:
# Whether to enforce the password policy.
# Defaults to 'false'.
#
#enabled: true
# Minimum accepted length for a password.
# Defaults to 0.
#
#minimum_length: 15
# Whether a password must contain at least one digit.
# Defaults to 'false'.
#
#require_digit: true
# Whether a password must contain at least one symbol.
# A symbol is any character that's not a number or a letter.
# Defaults to 'false'.
#
#require_symbol: true
# Whether a password must contain at least one lowercase letter.
# Defaults to 'false'.
#
#require_lowercase: true
# Whether a password must contain at least one lowercase letter.
# Defaults to 'false'.
#
#require_uppercase: true
ui_auth:
# The amount of time to allow a user-interactive authentication session
# to be active.
#
# This defaults to 0, meaning the user is queried for their credentials
# before every action, but this can be overridden to allow a single
# validation to be re-used. This weakens the protections afforded by
# the user-interactive authentication process, by allowing for multiple
# (and potentially different) operations to use the same validation session.
#
# This is ignored for potentially "dangerous" operations (including
# deactivating an account, modifying an account password, and
# adding a 3PID).
#
# Uncomment below to allow for credential validation to last for 15
# seconds.
#
#session_timeout: "15s"
# Configuration for sending emails from Synapse.
#
email:
# The hostname of the outgoing SMTP server to use. Defaults to 'localhost'.
#
#smtp_host: mail.server
# The port on the mail server for outgoing SMTP. Defaults to 25.
#
#smtp_port: 587
# Username/password for authentication to the SMTP server. By default, no
# authentication is attempted.
#
#smtp_user: "exampleusername"
#smtp_pass: "examplepassword"
# Uncomment the following to require TLS transport security for SMTP.
# By default, Synapse will connect over plain text, and will then switch to
# TLS via STARTTLS *if the SMTP server supports it*. If this option is set,
# Synapse will refuse to connect unless the server supports STARTTLS.
#
#require_transport_security: true
# notif_from defines the "From" address to use when sending emails.
# It must be set if email sending is enabled.
#
# The placeholder '%(app)s' will be replaced by the application name,
# which is normally 'app_name' (below), but may be overridden by the
# Matrix client application.
#
# Note that the placeholder must be written '%(app)s', including the
# trailing 's'.
#
#notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
# app_name defines the default value for '%(app)s' in notif_from and email
# subjects. It defaults to 'Matrix'.
#
#app_name: my_branded_matrix_server
# Uncomment the following to enable sending emails for messages that the user
# has missed. Disabled by default.
#
#enable_notifs: true
# Uncomment the following to disable automatic subscription to email
# notifications for new users. Enabled by default.
#
#notif_for_new_users: false
# Custom URL for client links within the email notifications. By default
# links will be based on "https://matrix.to".
#
# (This setting used to be called riot_base_url; the old name is still
# supported for backwards-compatibility but is now deprecated.)
#
#client_base_url: "http://localhost/riot"
# Configure the time that a validation email will expire after sending.
# Defaults to 1h.
#
#validation_token_lifetime: 15m
# The web client location to direct users to during an invite. This is passed
# to the identity server as the org.matrix.web_client_location key. Defaults
# to unset, giving no guidance to the identity server.
#
#invite_client_location: https://app.element.io
# Directory in which Synapse will try to find the template files below.
# If not set, or the files named below are not found within the template
# directory, default templates from within the Synapse package will be used.
#
# Synapse will look for the following templates in this directory:
#
# * The contents of email notifications of missed events: 'notif_mail.html' and
# 'notif_mail.txt'.
#
# * The contents of account expiry notice emails: 'notice_expiry.html' and
# 'notice_expiry.txt'.
#
# * The contents of password reset emails sent by the homeserver:
# 'password_reset.html' and 'password_reset.txt'
#
# * An HTML page that a user will see when they follow the link in the password
# reset email. The user will be asked to confirm the action before their
# password is reset: 'password_reset_confirmation.html'
#
# * HTML pages for success and failure that a user will see when they confirm
# the password reset flow using the page above: 'password_reset_success.html'
# and 'password_reset_failure.html'
#
# * The contents of address verification emails sent during registration:
# 'registration.html' and 'registration.txt'
#
# * HTML pages for success and failure that a user will see when they follow
# the link in an address verification email sent during registration:
# 'registration_success.html' and 'registration_failure.html'
#
# * The contents of address verification emails sent when an address is added
# to a Matrix account: 'add_threepid.html' and 'add_threepid.txt'
#
# * HTML pages for success and failure that a user will see when they follow
# the link in an address verification email sent when an address is added
# to a Matrix account: 'add_threepid_success.html' and
# 'add_threepid_failure.html'
#
# You can see the default templates at:
# https://github.com/matrix-org/synapse/tree/master/synapse/res/templates
#
#template_dir: "res/templates"
# Subjects to use when sending emails from Synapse.
#
# The placeholder '%(app)s' will be replaced with the value of the 'app_name'
# setting above, or by a value dictated by the Matrix client application.
#
# If a subject isn't overridden in this configuration file, the value used as
# its example will be used.
#
#subjects:
# Subjects for notification emails.
#
# On top of the '%(app)s' placeholder, these can use the following
# placeholders:
#
# * '%(person)s', which will be replaced by the display name of the user(s)
# that sent the message(s), e.g. "Alice and Bob".
# * '%(room)s', which will be replaced by the name of the room the
# message(s) have been sent to, e.g. "My super room".
#
# See the example provided for each setting to see which placeholder can be
# used and how to use them.
#
# Subject to use to notify about one message from one or more user(s) in a
# room which has a name.
#message_from_person_in_room: "[%(app)s] You have a message on %(app)s from %(person)s in the %(room)s room..."
#
# Subject to use to notify about one message from one or more user(s) in a
# room which doesn't have a name.
#message_from_person: "[%(app)s] You have a message on %(app)s from %(person)s..."
#
# Subject to use to notify about multiple messages from one or more users in
# a room which doesn't have a name.
#messages_from_person: "[%(app)s] You have messages on %(app)s from %(person)s..."
#
# Subject to use to notify about multiple messages in a room which has a
# name.
#messages_in_room: "[%(app)s] You have messages on %(app)s in the %(room)s room..."
#
# Subject to use to notify about multiple messages in multiple rooms.
#messages_in_room_and_others: "[%(app)s] You have messages on %(app)s in the %(room)s room and others..."
#
# Subject to use to notify about multiple messages from multiple persons in
# multiple rooms. This is similar to the setting above except it's used when
# the room in which the notification was triggered has no name.
#messages_from_person_and_others: "[%(app)s] You have messages on %(app)s from %(person)s and others..."
#
# Subject to use to notify about an invite to a room which has a name.
#invite_from_person_to_room: "[%(app)s] %(person)s has invited you to join the %(room)s room on %(app)s..."
#
# Subject to use to notify about an invite to a room which doesn't have a
# name.
#invite_from_person: "[%(app)s] %(person)s has invited you to chat on %(app)s..."
# Subject for emails related to account administration.
#
# On top of the '%(app)s' placeholder, these one can use the
# '%(server_name)s' placeholder, which will be replaced by the value of the
# 'server_name' setting in your Synapse configuration.
#
# Subject to use when sending a password reset email.
#password_reset: "[%(server_name)s] Password reset"
#
# Subject to use when sending a verification email to assert an address's
# ownership.
#email_validation: "[%(server_name)s] Validate your email"
# Password providers allow homeserver administrators to integrate
# their Synapse installation with existing authentication methods
# ex. LDAP, external tokens, etc.
#
# For more information and known implementations, please see
# https://matrix-org.github.io/synapse/latest/password_auth_providers.html
#
# Note: instances wishing to use SAML or CAS authentication should
# instead use the `saml2_config` or `cas_config` options,
# respectively.
#
password_providers:
# # Example config for an LDAP auth provider
# - module: "ldap_auth_provider.LdapAuthProvider"
# config:
# enabled: true
# uri: "ldap://ldap.example.com:389"
# start_tls: true
# base: "ou=users,dc=example,dc=com"
# attributes:
# uid: "cn"
# mail: "email"
# name: "givenName"
# #bind_dn:
# #bind_password:
# #filter: "(objectClass=posixAccount)"
## Push ##
push:
# Clients requesting push notifications can either have the body of
# the message sent in the notification poke along with other details
# like the sender, or just the event ID and room ID (`event_id_only`).
# If clients choose the former, this option controls whether the
# notification request includes the content of the event (other details
# like the sender are still included). For `event_id_only` push, it
# has no effect.
#
# For modern android devices the notification content will still appear
# because it is loaded by the app. iPhone, however will send a
# notification saying only that a message arrived and who it came from.
#
# The default value is "true" to include message details. Uncomment to only
# include the event ID and room ID in push notification payloads.
#
#include_content: false
# When a push notification is received, an unread count is also sent.
# This number can either be calculated as the number of unread messages
# for the user, or the number of *rooms* the user has unread messages in.
#
# The default value is "true", meaning push clients will see the number of
# rooms with unread messages in them. Uncomment to instead send the number
# of unread messages.
#
#group_unread_count_by_room: false
## Rooms ##
# Controls whether locally-created rooms should be end-to-end encrypted by
# default.
#
# Possible options are "all", "invite", and "off". They are defined as:
#
# * "all": any locally-created room
# * "invite": any room created with the "private_chat" or "trusted_private_chat"
# room creation presets
# * "off": this option will take no effect
#
# The default value is "off".
#
# Note that this option will only affect rooms created after it is set. It
# will also not affect rooms created by other servers.
#
#encryption_enabled_by_default_for_room_type: invite
# Uncomment to allow non-server-admin users to create groups on this server
#
#enable_group_creation: true
# If enabled, non server admins can only create groups with local parts
# starting with this prefix
#
#group_creation_prefix: "unofficial_"
# User Directory configuration
#
user_directory:
# Defines whether users can search the user directory. If false then
# empty responses are returned to all queries. Defaults to true.
#
# Uncomment to disable the user directory.
#
#enabled: false
# Defines whether to search all users visible to your HS when searching
# the user directory, rather than limiting to users visible in public
# rooms. Defaults to false.
#
# If you set it true, you'll have to rebuild the user_directory search
# indexes, see:
# https://matrix-org.github.io/synapse/latest/user_directory.html
#
# Uncomment to return search results containing all known users, even if that
# user does not share a room with the requester.
#
#search_all_users: true
# Defines whether to prefer local users in search query results.
# If True, local users are more likely to appear above remote users
# when searching the user directory. Defaults to false.
#
# Uncomment to prefer local over remote users in user directory search
# results.
#
#prefer_local_users: true
# User Consent configuration
#
# for detailed instructions, see
# https://matrix-org.github.io/synapse/latest/consent_tracking.html
#
# Parts of this section are required if enabling the 'consent' resource under
# 'listeners', in particular 'template_dir' and 'version'.
#
# 'template_dir' gives the location of the templates for the HTML forms.
# This directory should contain one subdirectory per language (eg, 'en', 'fr'),
# and each language directory should contain the policy document (named as
# '<version>.html') and a success page (success.html).
#
# 'version' specifies the 'current' version of the policy document. It defines
# the version to be served by the consent resource if there is no 'v'
# parameter.
#
# 'server_notice_content', if enabled, will send a user a "Server Notice"
# asking them to consent to the privacy policy. The 'server_notices' section
# must also be configured for this to work. Notices will *not* be sent to
# guest users unless 'send_server_notice_to_guests' is set to true.
#
# 'block_events_error', if set, will block any attempts to send events
# until the user consents to the privacy policy. The value of the setting is
# used as the text of the error.
#
# 'require_at_registration', if enabled, will add a step to the registration
# process, similar to how captcha works. Users will be required to accept the
# policy before their account is created.
#
# 'policy_name' is the display name of the policy users will see when registering
# for an account. Has no effect unless `require_at_registration` is enabled.
# Defaults to "Privacy Policy".
#
#user_consent:
# template_dir: res/templates/privacy
# version: 1.0
# server_notice_content:
# msgtype: m.text
# body: >-
# To continue using this homeserver you must review and agree to the
# terms and conditions at %(consent_uri)s
# send_server_notice_to_guests: true
# block_events_error: >-
# To continue using this homeserver you must review and agree to the
# terms and conditions at %(consent_uri)s
# require_at_registration: false
# policy_name: Privacy Policy
#
# Settings for local room and user statistics collection. See
# https://matrix-org.github.io/synapse/latest/room_and_user_statistics.html.
#
stats:
# Uncomment the following to disable room and user statistics. Note that doing
# so may cause certain features (such as the room directory) not to work
# correctly.
#
#enabled: false
# Server Notices room configuration
#
# Uncomment this section to enable a room which can be used to send notices
# from the server to users. It is a special room which cannot be left; notices
# come from a special "notices" user id.
#
# If you uncomment this section, you *must* define the system_mxid_localpart
# setting, which defines the id of the user which will be used to send the
# notices.
#
# It's also possible to override the room name, the display name of the
# "notices" user, and the avatar for the user.
#
#server_notices:
# system_mxid_localpart: notices
# system_mxid_display_name: "Server Notices"
# system_mxid_avatar_url: "mxc://server.com/oumMVlgDnLYFaPVkExemNVVZ"
# room_name: "Server Notices"
# Uncomment to disable searching the public room list. When disabled
# blocks searching local and remote room lists for local and remote
# users by always returning an empty list for all queries.
#
#enable_room_list_search: false
# The `alias_creation` option controls who's allowed to create aliases
# on this server.
#
# The format of this option is a list of rules that contain globs that
# match against user_id, room_id and the new alias (fully qualified with
# server name). The action in the first rule that matches is taken,
# which can currently either be "allow" or "deny".
#
# Missing user_id/room_id/alias fields default to "*".
#
# If no rules match the request is denied. An empty list means no one
# can create aliases.
#
# Options for the rules include:
#
# user_id: Matches against the creator of the alias
# alias: Matches against the alias being created
# room_id: Matches against the room ID the alias is being pointed at
# action: Whether to "allow" or "deny" the request if the rule matches
#
# The default is:
#
#alias_creation_rules:
# - user_id: "*"
# alias: "*"
# room_id: "*"
# action: allow
# The `room_list_publication_rules` option controls who can publish and
# which rooms can be published in the public room list.
#
# The format of this option is the same as that for
# `alias_creation_rules`.
#
# If the room has one or more aliases associated with it, only one of
# the aliases needs to match the alias rule. If there are no aliases
# then only rules with `alias: *` match.
#
# If no rules match the request is denied. An empty list means no one
# can publish rooms.
#
# Options for the rules include:
#
# user_id: Matches against the creator of the alias
# room_id: Matches against the room ID being published
# alias: Matches against any current local or canonical aliases
# associated with the room
# action: Whether to "allow" or "deny" the request if the rule matches
#
# The default is:
#
#room_list_publication_rules:
# - user_id: "*"
# alias: "*"
# room_id: "*"
# action: allow
## Opentracing ##
# These settings enable opentracing, which implements distributed tracing.
# This allows you to observe the causal chains of events across servers
# including requests, key lookups etc., across any server running
# synapse or any other other services which supports opentracing
# (specifically those implemented with Jaeger).
#
opentracing:
# tracing is disabled by default. Uncomment the following line to enable it.
#
#enabled: true
# The list of homeservers we wish to send and receive span contexts and span baggage.
# See https://matrix-org.github.io/synapse/latest/opentracing.html.
#
# This is a list of regexes which are matched against the server_name of the
# homeserver.
#
# By default, it is empty, so no servers are matched.
#
#homeserver_whitelist:
# - ".*"
# A list of the matrix IDs of users whose requests will always be traced,
# even if the tracing system would otherwise drop the traces due to
# probabilistic sampling.
#
# By default, the list is empty.
#
#force_tracing_for_users:
# - "@user1:server_name"
# - "@user2:server_name"
# Jaeger can be configured to sample traces at different rates.
# All configuration options provided by Jaeger can be set here.
# Jaeger's configuration is mostly related to trace sampling which
# is documented here:
# https://www.jaegertracing.io/docs/latest/sampling/.
#
#jaeger_config:
# sampler:
# type: const
# param: 1
# logging:
# false
## Workers ##
# Disables sending of outbound federation transactions on the main process.
# Uncomment if using a federation sender worker.
#
#send_federation: false
# It is possible to run multiple federation sender workers, in which case the
# work is balanced across them.
#
# This configuration must be shared between all federation sender workers, and if
# changed all federation sender workers must be stopped at the same time and then
# started, to ensure that all instances are running with the same config (otherwise
# events may be dropped).
#
#federation_sender_instances:
# - federation_sender1
# When using workers this should be a map from `worker_name` to the
# HTTP replication listener of the worker, if configured.
#
#instance_map:
# worker1:
# host: localhost
# port: 8034
# Experimental: When using workers you can define which workers should
# handle event persistence and typing notifications. Any worker
# specified here must also be in the `instance_map`.
#
#stream_writers:
# events: worker1
# typing: worker1
# The worker that is used to run background tasks (e.g. cleaning up expired
# data). If not provided this defaults to the main process.
#
#run_background_tasks_on: worker1
# A shared secret used by the replication APIs to authenticate HTTP requests
# from workers.
#
# By default this is unused and traffic is not authenticated.
#
#worker_replication_secret: ""
# Configuration for Redis when using workers. This *must* be enabled when
# using workers (unless using old style direct TCP configuration).
#
redis:
# Uncomment the below to enable Redis support.
#
#enabled: true
# Optional host and port to use to connect to redis. Defaults to
# localhost and 6379
#
#host: localhost
#port: 6379
# Optional password if configured on the Redis instance
#
#password: <secret_password>
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="logging-sample-configuration-file"><a class="header" href="#logging-sample-configuration-file">Logging Sample Configuration File</a></h1>
<p>Below is a sample logging configuration file. This file can be tweaked to control how your
homeserver will output logs. A restart of the server is generally required to apply any
changes made to this file.</p>
<p>Note that the contents below are <em>not</em> intended to be copied and used as the basis for
a real homeserver.yaml. Instead, if you are starting from scratch, please generate
a fresh config using Synapse by following the instructions in
<a href="usage/configuration/../../setup/installation.html">Installation</a>.</p>
<pre><code class="language-yaml"># Log configuration for Synapse.
#
# This is a YAML file containing a standard Python logging configuration
# dictionary. See [1] for details on the valid settings.
#
# Synapse also supports structured logging for machine readable logs which can
# be ingested by ELK stacks. See [2] for details.
#
# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema
# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html
version: 1
formatters:
precise:
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
handlers:
file:
class: logging.handlers.TimedRotatingFileHandler
formatter: precise
filename: /var/log/matrix-synapse/homeserver.log
when: midnight
backupCount: 3 # Does not include the current log file.
encoding: utf8
# Default to buffering writes to log file for efficiency. This means that
# will be a delay for INFO/DEBUG logs to get written, but WARNING/ERROR
# logs will still be flushed immediately.
buffer:
class: synapse.logging.handlers.PeriodicallyFlushingMemoryHandler
target: file
# The capacity is the number of log lines that are buffered before
# being written to disk. Increasing this will lead to better
# performance, at the expensive of it taking longer for log lines to
# be written to disk.
capacity: 10
flushLevel: 30 # Flush for WARNING logs as well
# The period of time, in seconds, between forced flushes.
# Messages will not be delayed for longer than this time.
period: 5
# A handler that writes logs to stderr. Unused by default, but can be used
# instead of "buffer" and "file" in the logger handlers.
console:
class: logging.StreamHandler
formatter: precise
loggers:
synapse.storage.SQL:
# beware: increasing this to DEBUG will make synapse log sensitive
# information such as access tokens.
level: INFO
twisted:
# We send the twisted logging directly to the file handler,
# to work around https://github.com/matrix-org/synapse/issues/3471
# when using "buffer" logger. Use "console" to log to stderr instead.
handlers: [file]
propagate: false
root:
level: INFO
# Write logs to the `buffer` handler, which will buffer them together in memory,
# then write them to a file.
#
# Replace "buffer" with "console" to log to stderr instead. (Note that you'll
# also need to update the configuration for the `twisted` logger above, in
# this case.)
#
handlers: [buffer]
disable_existing_loggers: false
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="structured-logging"><a class="header" href="#structured-logging">Structured Logging</a></h1>
<p>A structured logging system can be useful when your logs are destined for a
machine to parse and process. By maintaining its machine-readable characteristics,
it enables more efficient searching and aggregations when consumed by software
such as the "ELK stack".</p>
<p>Synapse's structured logging system is configured via the file that Synapse's
<code>log_config</code> config option points to. The file should include a formatter which
uses the <code>synapse.logging.TerseJsonFormatter</code> class included with Synapse and a
handler which uses the above formatter.</p>
<p>There is also a <code>synapse.logging.JsonFormatter</code> option which does not include
a timestamp in the resulting JSON. This is useful if the log ingester adds its
own timestamp.</p>
<p>A structured logging configuration looks similar to the following:</p>
<pre><code class="language-yaml">version: 1
formatters:
structured:
class: synapse.logging.TerseJsonFormatter
handlers:
file:
class: logging.handlers.TimedRotatingFileHandler
formatter: structured
filename: /path/to/my/logs/homeserver.log
when: midnight
backupCount: 3 # Does not include the current log file.
encoding: utf8
loggers:
synapse:
level: INFO
handlers: [remote]
synapse.storage.SQL:
level: WARNING
</code></pre>
<p>The above logging config will set Synapse as 'INFO' logging level by default,
with the SQL layer at 'WARNING', and will log to a file, stored as JSON.</p>
<p>It is also possible to figure Synapse to log to a remote endpoint by using the
<code>synapse.logging.RemoteHandler</code> class included with Synapse. It takes the
following arguments:</p>
<ul>
<li><code>host</code>: Hostname or IP address of the log aggregator.</li>
<li><code>port</code>: Numerical port to contact on the host.</li>
<li><code>maximum_buffer</code>: (Optional, defaults to 1000) The maximum buffer size to allow.</li>
</ul>
<p>A remote structured logging configuration looks similar to the following:</p>
<pre><code class="language-yaml">version: 1
formatters:
structured:
class: synapse.logging.TerseJsonFormatter
handlers:
remote:
class: synapse.logging.RemoteHandler
formatter: structured
host: 10.1.2.3
port: 9999
loggers:
synapse:
level: INFO
handlers: [remote]
synapse.storage.SQL:
level: WARNING
</code></pre>
<p>The above logging config will set Synapse as 'INFO' logging level by default,
with the SQL layer at 'WARNING', and will log JSON formatted messages to a
remote endpoint at 10.1.2.3:9999.</p>
<h2 id="upgrading-from-legacy-structured-logging-configuration"><a class="header" href="#upgrading-from-legacy-structured-logging-configuration">Upgrading from legacy structured logging configuration</a></h2>
<p>Versions of Synapse prior to v1.23.0 included a custom structured logging
configuration which is deprecated. It used a <code>structured: true</code> flag and
configured <code>drains</code> instead of <code>handlers</code> and <code>formatters</code>.</p>
<p>Synapse currently automatically converts the old configuration to the new
configuration, but this will be removed in a future version of Synapse. The
following reference can be used to update your configuration. Based on the drain
<code>type</code>, we can pick a new handler:</p>
<ol>
<li>For a type of <code>console</code>, <code>console_json</code>, or <code>console_json_terse</code>: a handler
with a class of <code>logging.StreamHandler</code> and a <code>stream</code> of <code>ext://sys.stdout</code>
or <code>ext://sys.stderr</code> should be used.</li>
<li>For a type of <code>file</code> or <code>file_json</code>: a handler of <code>logging.FileHandler</code> with
a location of the file path should be used.</li>
<li>For a type of <code>network_json_terse</code>: a handler of <code>synapse.logging.RemoteHandler</code>
with the host and port should be used.</li>
</ol>
<p>Then based on the drain <code>type</code> we can pick a new formatter:</p>
<ol>
<li>For a type of <code>console</code> or <code>file</code> no formatter is necessary.</li>
<li>For a type of <code>console_json</code> or <code>file_json</code>: a formatter of
<code>synapse.logging.JsonFormatter</code> should be used.</li>
<li>For a type of <code>console_json_terse</code> or <code>network_json_terse</code>: a formatter of
<code>synapse.logging.TerseJsonFormatter</code> should be used.</li>
</ol>
<p>For each new handler and formatter they should be added to the logging configuration
and then assigned to either a logger or the root logger.</p>
<p>An example legacy configuration:</p>
<pre><code class="language-yaml">structured: true
loggers:
synapse:
level: INFO
synapse.storage.SQL:
level: WARNING
drains:
console:
type: console
location: stdout
file:
type: file_json
location: homeserver.log
</code></pre>
<p>Would be converted into a new configuration:</p>
<pre><code class="language-yaml">version: 1
formatters:
json:
class: synapse.logging.JsonFormatter
handlers:
console:
class: logging.StreamHandler
location: ext://sys.stdout
file:
class: logging.FileHandler
formatter: json
filename: homeserver.log
loggers:
synapse:
level: INFO
handlers: [console, file]
synapse.storage.SQL:
level: WARNING
</code></pre>
<p>The new logging configuration is a bit more verbose, but significantly more
flexible. It allows for configuration that were not previously possible, such as
sending plain logs over the network, or using different handlers for different
modules.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="user-authentication"><a class="header" href="#user-authentication">User Authentication</a></h1>
<p>Synapse supports multiple methods of authenticating users, either out-of-the-box or through custom pluggable
authentication modules.</p>
<p>Included in Synapse is support for authenticating users via:</p>
<ul>
<li>A username and password.</li>
<li>An email address and password.</li>
<li>Single Sign-On through the SAML, Open ID Connect or CAS protocols.</li>
<li>JSON Web Tokens.</li>
<li>An administrator's shared secret.</li>
</ul>
<p>Synapse can additionally be extended to support custom authentication schemes through optional "password auth provider"
modules.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="configuring-synapse-to-authenticate-against-an-openid-connect-provider"><a class="header" href="#configuring-synapse-to-authenticate-against-an-openid-connect-provider">Configuring Synapse to authenticate against an OpenID Connect provider</a></h1>
<p>Synapse can be configured to use an OpenID Connect Provider (OP) for
authentication, instead of its own local password database.</p>
<p>Any OP should work with Synapse, as long as it supports the authorization code
flow. There are a few options for that:</p>
<ul>
<li>
<p>start a local OP. Synapse has been tested with <a href="https://www.ory.sh/docs/hydra/">Hydra</a> and
<a href="https://github.com/dexidp/dex">Dex</a>. Note that for an OP to work, it should be served under a
secure (HTTPS) origin. A certificate signed with a self-signed, locally
trusted CA should work. In that case, start Synapse with a <code>SSL_CERT_FILE</code>
environment variable set to the path of the CA.</p>
</li>
<li>
<p>set up a SaaS OP, like <a href="https://developers.google.com/identity/protocols/oauth2/openid-connect">Google</a>, <a href="https://auth0.com/">Auth0</a> or
<a href="https://www.okta.com/">Okta</a>. Synapse has been tested with Auth0 and Google.</p>
</li>
</ul>
<p>It may also be possible to use other OAuth2 providers which provide the
<a href="https://tools.ietf.org/html/rfc6749#section-4.1">authorization code grant type</a>,
such as <a href="https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps">Github</a>.</p>
<h2 id="preparing-synapse"><a class="header" href="#preparing-synapse">Preparing Synapse</a></h2>
<p>The OpenID integration in Synapse uses the
<a href="https://pypi.org/project/Authlib/"><code>authlib</code></a> library, which must be installed
as follows:</p>
<ul>
<li>
<p>The relevant libraries are included in the Docker images and Debian packages
provided by <code>matrix.org</code> so no further action is needed.</p>
</li>
<li>
<p>If you installed Synapse into a virtualenv, run <code>/path/to/env/bin/pip install matrix-synapse[oidc]</code> to install the necessary dependencies.</p>
</li>
<li>
<p>For other installation mechanisms, see the documentation provided by the
maintainer.</p>
</li>
</ul>
<p>To enable the OpenID integration, you should then add a section to the <code>oidc_providers</code>
setting in your configuration file (or uncomment one of the existing examples).
See <a href="./sample_config.yaml">sample_config.yaml</a> for some sample settings, as well as
the text below for example configurations for specific providers.</p>
<h2 id="sample-configs"><a class="header" href="#sample-configs">Sample configs</a></h2>
<p>Here are a few configs for providers that should work with Synapse.</p>
<h3 id="microsoft-azure-active-directory"><a class="header" href="#microsoft-azure-active-directory">Microsoft Azure Active Directory</a></h3>
<p>Azure AD can act as an OpenID Connect Provider. Register a new application under
<em>App registrations</em> in the Azure AD management console. The RedirectURI for your
application should point to your matrix server:
<code>[synapse public baseurl]/_synapse/client/oidc/callback</code></p>
<p>Go to <em>Certificates & secrets</em> and register a new client secret. Make note of your
Directory (tenant) ID as it will be used in the Azure links.
Edit your Synapse config file and change the <code>oidc_config</code> section:</p>
<pre><code class="language-yaml">oidc_providers:
- idp_id: microsoft
idp_name: Microsoft
issuer: "https://login.microsoftonline.com/<tenant id>/v2.0"
client_id: "<client id>"
client_secret: "<client secret>"
scopes: ["openid", "profile"]
authorization_endpoint: "https://login.microsoftonline.com/<tenant id>/oauth2/v2.0/authorize"
token_endpoint: "https://login.microsoftonline.com/<tenant id>/oauth2/v2.0/token"
userinfo_endpoint: "https://graph.microsoft.com/oidc/userinfo"
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username.split('@')[0] }}"
display_name_template: "{{ user.name }}"
</code></pre>
<h3 id="a-hrefhttpsgithubcomdexidpdexdexa"><a class="header" href="#a-hrefhttpsgithubcomdexidpdexdexa"><a href="https://github.com/dexidp/dex">Dex</a></a></h3>
<p><a href="https://github.com/dexidp/dex">Dex</a> is a simple, open-source, certified OpenID Connect Provider.
Although it is designed to help building a full-blown provider with an
external database, it can be configured with static passwords in a config file.</p>
<p>Follow the <a href="https://dexidp.io/docs/getting-started/">Getting Started guide</a>
to install Dex.</p>
<p>Edit <code>examples/config-dev.yaml</code> config file from the Dex repo to add a client:</p>
<pre><code class="language-yaml">staticClients:
- id: synapse
secret: secret
redirectURIs:
- '[synapse public baseurl]/_synapse/client/oidc/callback'
name: 'Synapse'
</code></pre>
<p>Run with <code>dex serve examples/config-dev.yaml</code>.</p>
<p>Synapse config:</p>
<pre><code class="language-yaml">oidc_providers:
- idp_id: dex
idp_name: "My Dex server"
skip_verification: true # This is needed as Dex is served on an insecure endpoint
issuer: "http://127.0.0.1:5556/dex"
client_id: "synapse"
client_secret: "secret"
scopes: ["openid", "profile"]
user_mapping_provider:
config:
localpart_template: "{{ user.name }}"
display_name_template: "{{ user.name|capitalize }}"
</code></pre>
<h3 id="a-hrefhttpswwwkeycloakorgdocslatestserver_adminsso-protocolskeycloaka"><a class="header" href="#a-hrefhttpswwwkeycloakorgdocslatestserver_adminsso-protocolskeycloaka"><a href="https://www.keycloak.org/docs/latest/server_admin/#sso-protocols">Keycloak</a></a></h3>
<p><a href="https://www.keycloak.org/docs/latest/server_admin/#sso-protocols">Keycloak</a> is an opensource IdP maintained by Red Hat.</p>
<p>Follow the <a href="https://www.keycloak.org/getting-started">Getting Started Guide</a> to install Keycloak and set up a realm.</p>
<ol>
<li>
<p>Click <code>Clients</code> in the sidebar and click <code>Create</code></p>
</li>
<li>
<p>Fill in the fields as below:</p>
</li>
</ol>
<table><thead><tr><th>Field</th><th>Value</th></tr></thead><tbody>
<tr><td>Client ID</td><td><code>synapse</code></td></tr>
<tr><td>Client Protocol</td><td><code>openid-connect</code></td></tr>
</tbody></table>
<ol start="3">
<li>Click <code>Save</code></li>
<li>Fill in the fields as below:</li>
</ol>
<table><thead><tr><th>Field</th><th>Value</th></tr></thead><tbody>
<tr><td>Client ID</td><td><code>synapse</code></td></tr>
<tr><td>Enabled</td><td><code>On</code></td></tr>
<tr><td>Client Protocol</td><td><code>openid-connect</code></td></tr>
<tr><td>Access Type</td><td><code>confidential</code></td></tr>
<tr><td>Valid Redirect URIs</td><td><code>[synapse public baseurl]/_synapse/client/oidc/callback</code></td></tr>
</tbody></table>
<ol start="5">
<li>Click <code>Save</code></li>
<li>On the Credentials tab, update the fields:</li>
</ol>
<table><thead><tr><th>Field</th><th>Value</th></tr></thead><tbody>
<tr><td>Client Authenticator</td><td><code>Client ID and Secret</code></td></tr>
</tbody></table>
<ol start="7">
<li>Click <code>Regenerate Secret</code></li>
<li>Copy Secret</li>
</ol>
<pre><code class="language-yaml">oidc_providers:
- idp_id: keycloak
idp_name: "My KeyCloak server"
issuer: "https://127.0.0.1:8443/auth/realms/{realm_name}"
client_id: "synapse"
client_secret: "copy secret generated from above"
scopes: ["openid", "profile"]
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
</code></pre>
<h3 id="a-hrefhttpsauth0comauth0a"><a class="header" href="#a-hrefhttpsauth0comauth0a"><a href="https://auth0.com/">Auth0</a></a></h3>
<ol>
<li>
<p>Create a regular web application for Synapse</p>
</li>
<li>
<p>Set the Allowed Callback URLs to <code>[synapse public baseurl]/_synapse/client/oidc/callback</code></p>
</li>
<li>
<p>Add a rule to add the <code>preferred_username</code> claim.</p>
<details>
<summary>Code sample</summary>
<pre><code class="language-js">function addPersistenceAttribute(user, context, callback) {
user.user_metadata = user.user_metadata || {};
user.user_metadata.preferred_username = user.user_metadata.preferred_username || user.user_id;
context.idToken.preferred_username = user.user_metadata.preferred_username;
auth0.users.updateUserMetadata(user.user_id, user.user_metadata)
.then(function(){
callback(null, user, context);
})
.catch(function(err){
callback(err);
});
}
</code></pre>
</li>
</ol>
</details>
<p>Synapse config:</p>
<pre><code class="language-yaml">oidc_providers:
- idp_id: auth0
idp_name: Auth0
issuer: "https://your-tier.eu.auth0.com/" # TO BE FILLED
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
scopes: ["openid", "profile"]
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
</code></pre>
<h3 id="github"><a class="header" href="#github">GitHub</a></h3>
<p>GitHub is a bit special as it is not an OpenID Connect compliant provider, but
just a regular OAuth2 provider.</p>
<p>The <a href="https://developer.github.com/v3/users/#get-the-authenticated-user"><code>/user</code> API endpoint</a>
can be used to retrieve information on the authenticated user. As the Synapse
login mechanism needs an attribute to uniquely identify users, and that endpoint
does not return a <code>sub</code> property, an alternative <code>subject_claim</code> has to be set.</p>
<ol>
<li>Create a new OAuth application: https://github.com/settings/applications/new.</li>
<li>Set the callback URL to <code>[synapse public baseurl]/_synapse/client/oidc/callback</code>.</li>
</ol>
<p>Synapse config:</p>
<pre><code class="language-yaml">oidc_providers:
- idp_id: github
idp_name: Github
idp_brand: "github" # optional: styling hint for clients
discover: false
issuer: "https://github.com/"
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
authorization_endpoint: "https://github.com/login/oauth/authorize"
token_endpoint: "https://github.com/login/oauth/access_token"
userinfo_endpoint: "https://api.github.com/user"
scopes: ["read:user"]
user_mapping_provider:
config:
subject_claim: "id"
localpart_template: "{{ user.login }}"
display_name_template: "{{ user.name }}"
</code></pre>
<h3 id="a-hrefhttpsdevelopersgooglecomidentityprotocolsoauth2openid-connectgooglea"><a class="header" href="#a-hrefhttpsdevelopersgooglecomidentityprotocolsoauth2openid-connectgooglea"><a href="https://developers.google.com/identity/protocols/oauth2/openid-connect">Google</a></a></h3>
<ol>
<li>Set up a project in the Google API Console (see
https://developers.google.com/identity/protocols/oauth2/openid-connect#appsetup).</li>
<li>add an "OAuth Client ID" for a Web Application under "Credentials".</li>
<li>Copy the Client ID and Client Secret, and add the following to your synapse config:
<pre><code class="language-yaml">oidc_providers:
- idp_id: google
idp_name: Google
idp_brand: "google" # optional: styling hint for clients
issuer: "https://accounts.google.com/"
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
scopes: ["openid", "profile"]
user_mapping_provider:
config:
localpart_template: "{{ user.given_name|lower }}"
display_name_template: "{{ user.name }}"
</code></pre>
</li>
<li>Back in the Google console, add this Authorized redirect URI: <code>[synapse public baseurl]/_synapse/client/oidc/callback</code>.</li>
</ol>
<h3 id="twitch"><a class="header" href="#twitch">Twitch</a></h3>
<ol>
<li>Setup a developer account on <a href="https://dev.twitch.tv/">Twitch</a></li>
<li>Obtain the OAuth 2.0 credentials by <a href="https://dev.twitch.tv/console/apps/">creating an app</a></li>
<li>Add this OAuth Redirect URL: <code>[synapse public baseurl]/_synapse/client/oidc/callback</code></li>
</ol>
<p>Synapse config:</p>
<pre><code class="language-yaml">oidc_providers:
- idp_id: twitch
idp_name: Twitch
issuer: "https://id.twitch.tv/oauth2/"
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
client_auth_method: "client_secret_post"
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
</code></pre>
<h3 id="gitlab"><a class="header" href="#gitlab">GitLab</a></h3>
<ol>
<li>Create a <a href="https://gitlab.com/profile/applications">new application</a>.</li>
<li>Add the <code>read_user</code> and <code>openid</code> scopes.</li>
<li>Add this Callback URL: <code>[synapse public baseurl]/_synapse/client/oidc/callback</code></li>
</ol>
<p>Synapse config:</p>
<pre><code class="language-yaml">oidc_providers:
- idp_id: gitlab
idp_name: Gitlab
idp_brand: "gitlab" # optional: styling hint for clients
issuer: "https://gitlab.com/"
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
client_auth_method: "client_secret_post"
scopes: ["openid", "read_user"]
user_profile_method: "userinfo_endpoint"
user_mapping_provider:
config:
localpart_template: '{{ user.nickname }}'
display_name_template: '{{ user.name }}'
</code></pre>
<h3 id="facebook"><a class="header" href="#facebook">Facebook</a></h3>
<p>Like Github, Facebook provide a custom OAuth2 API rather than an OIDC-compliant
one so requires a little more configuration.</p>
<ol start="0">
<li>You will need a Facebook developer account. You can register for one
<a href="https://developers.facebook.com/async/registration/">here</a>.</li>
<li>On the <a href="https://developers.facebook.com/apps/">apps</a> page of the developer
console, "Create App", and choose "Build Connected Experiences".</li>
<li>Once the app is created, add "Facebook Login" and choose "Web". You don't
need to go through the whole form here.</li>
<li>In the left-hand menu, open "Products"/"Facebook Login"/"Settings".
<ul>
<li>Add <code>[synapse public baseurl]/_synapse/client/oidc/callback</code> as an OAuth Redirect
URL.</li>
</ul>
</li>
<li>In the left-hand menu, open "Settings/Basic". Here you can copy the "App ID"
and "App Secret" for use below.</li>
</ol>
<p>Synapse config:</p>
<pre><code class="language-yaml"> - idp_id: facebook
idp_name: Facebook
idp_brand: "facebook" # optional: styling hint for clients
discover: false
issuer: "https://facebook.com"
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
scopes: ["openid", "email"]
authorization_endpoint: https://facebook.com/dialog/oauth
token_endpoint: https://graph.facebook.com/v9.0/oauth/access_token
user_profile_method: "userinfo_endpoint"
userinfo_endpoint: "https://graph.facebook.com/v9.0/me?fields=id,name,email,picture"
user_mapping_provider:
config:
subject_claim: "id"
display_name_template: "{{ user.name }}"
</code></pre>
<p>Relevant documents:</p>
<ul>
<li>https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow</li>
<li>Using Facebook's Graph API: https://developers.facebook.com/docs/graph-api/using-graph-api/</li>
<li>Reference to the User endpoint: https://developers.facebook.com/docs/graph-api/reference/user</li>
</ul>
<h3 id="gitea"><a class="header" href="#gitea">Gitea</a></h3>
<p>Gitea is, like Github, not an OpenID provider, but just an OAuth2 provider.</p>
<p>The <a href="https://try.gitea.io/api/swagger#/user/userGetCurrent"><code>/user</code> API endpoint</a>
can be used to retrieve information on the authenticated user. As the Synapse
login mechanism needs an attribute to uniquely identify users, and that endpoint
does not return a <code>sub</code> property, an alternative <code>subject_claim</code> has to be set.</p>
<ol>
<li>Create a new application.</li>
<li>Add this Callback URL: <code>[synapse public baseurl]/_synapse/client/oidc/callback</code></li>
</ol>
<p>Synapse config:</p>
<pre><code class="language-yaml">oidc_providers:
- idp_id: gitea
idp_name: Gitea
discover: false
issuer: "https://your-gitea.com/"
client_id: "your-client-id" # TO BE FILLED
client_secret: "your-client-secret" # TO BE FILLED
client_auth_method: client_secret_post
scopes: [] # Gitea doesn't support Scopes
authorization_endpoint: "https://your-gitea.com/login/oauth/authorize"
token_endpoint: "https://your-gitea.com/login/oauth/access_token"
userinfo_endpoint: "https://your-gitea.com/api/v1/user"
user_mapping_provider:
config:
subject_claim: "id"
localpart_template: "{{ user.login }}"
display_name_template: "{{ user.full_name }}"
</code></pre>
<h3 id="xwiki"><a class="header" href="#xwiki">XWiki</a></h3>
<p>Install <a href="https://extensions.xwiki.org/xwiki/bin/view/Extension/OpenID%20Connect/OpenID%20Connect%20Provider/">OpenID Connect Provider</a> extension in your <a href="https://www.xwiki.org">XWiki</a> instance.</p>
<p>Synapse config:</p>
<pre><code class="language-yaml">oidc_providers:
- idp_id: xwiki
idp_name: "XWiki"
issuer: "https://myxwikihost/xwiki/oidc/"
client_id: "your-client-id" # TO BE FILLED
client_auth_method: none
scopes: ["openid", "profile"]
user_profile_method: "userinfo_endpoint"
user_mapping_provider:
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
</code></pre>
<h3 id="apple"><a class="header" href="#apple">Apple</a></h3>
<p>Configuring "Sign in with Apple" (SiWA) requires an Apple Developer account.</p>
<p>You will need to create a new "Services ID" for SiWA, and create and download a
private key with "SiWA" enabled.</p>
<p>As well as the private key file, you will need:</p>
<ul>
<li>Client ID: the "identifier" you gave the "Services ID"</li>
<li>Team ID: a 10-character ID associated with your developer account.</li>
<li>Key ID: the 10-character identifier for the key.</li>
</ul>
<p>https://help.apple.com/developer-account/?lang=en#/dev77c875b7e has more
documentation on setting up SiWA.</p>
<p>The synapse config will look like this:</p>
<pre><code class="language-yaml"> - idp_id: apple
idp_name: Apple
issuer: "https://appleid.apple.com"
client_id: "your-client-id" # Set to the "identifier" for your "ServicesID"
client_auth_method: "client_secret_post"
client_secret_jwt_key:
key_file: "/path/to/AuthKey_KEYIDCODE.p8" # point to your key file
jwt_header:
alg: ES256
kid: "KEYIDCODE" # Set to the 10-char Key ID
jwt_payload:
iss: TEAMIDCODE # Set to the 10-char Team ID
scopes: ["name", "email", "openid"]
authorization_endpoint: https://appleid.apple.com/auth/authorize?response_mode=form_post
user_mapping_provider:
config:
email_template: "{{ user.email }}"
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="sso-mapping-providers"><a class="header" href="#sso-mapping-providers">SSO Mapping Providers</a></h1>
<p>A mapping provider is a Python class (loaded via a Python module) that
works out how to map attributes of a SSO response to Matrix-specific
user attributes. Details such as user ID localpart, displayname, and even avatar
URLs are all things that can be mapped from talking to a SSO service.</p>
<p>As an example, a SSO service may return the email address
"john.smith@example.com" for a user, whereas Synapse will need to figure out how
to turn that into a displayname when creating a Matrix user for this individual.
It may choose <code>John Smith</code>, or <code>Smith, John [Example.com]</code> or any number of
variations. As each Synapse configuration may want something different, this is
where SAML mapping providers come into play.</p>
<p>SSO mapping providers are currently supported for OpenID and SAML SSO
configurations. Please see the details below for how to implement your own.</p>
<p>It is up to the mapping provider whether the user should be assigned a predefined
Matrix ID based on the SSO attributes, or if the user should be allowed to
choose their own username.</p>
<p>In the first case - where users are automatically allocated a Matrix ID - it is
the responsibility of the mapping provider to normalise the SSO attributes and
map them to a valid Matrix ID. The <a href="https://matrix.org/docs/spec/appendices#user-identifiers">specification for Matrix
IDs</a> has some
information about what is considered valid.</p>
<p>If the mapping provider does not assign a Matrix ID, then Synapse will
automatically serve an HTML page allowing the user to pick their own username.</p>
<p>External mapping providers are provided to Synapse in the form of an external
Python module. You can retrieve this module from <a href="https://pypi.org">PyPI</a> or elsewhere,
but it must be importable via Synapse (e.g. it must be in the same virtualenv
as Synapse). The Synapse config is then modified to point to the mapping provider
(and optionally provide additional configuration for it).</p>
<h2 id="openid-mapping-providers"><a class="header" href="#openid-mapping-providers">OpenID Mapping Providers</a></h2>
<p>The OpenID mapping provider can be customized by editing the
<code>oidc_config.user_mapping_provider.module</code> config option.</p>
<p><code>oidc_config.user_mapping_provider.config</code> allows you to provide custom
configuration options to the module. Check with the module's documentation for
what options it provides (if any). The options listed by default are for the
user mapping provider built in to Synapse. If using a custom module, you should
comment these options out and use those specified by the module instead.</p>
<h3 id="building-a-custom-openid-mapping-provider"><a class="header" href="#building-a-custom-openid-mapping-provider">Building a Custom OpenID Mapping Provider</a></h3>
<p>A custom mapping provider must specify the following methods:</p>
<ul>
<li><code>__init__(self, parsed_config)</code>
<ul>
<li>Arguments:
<ul>
<li><code>parsed_config</code> - A configuration object that is the return value of the
<code>parse_config</code> method. You should set any configuration options needed by
the module here.</li>
</ul>
</li>
</ul>
</li>
<li><code>parse_config(config)</code>
<ul>
<li>This method should have the <code>@staticmethod</code> decoration.</li>
<li>Arguments:
<ul>
<li><code>config</code> - A <code>dict</code> representing the parsed content of the
<code>oidc_config.user_mapping_provider.config</code> homeserver config option.
Runs on homeserver startup. Providers should extract and validate
any option values they need here.</li>
</ul>
</li>
<li>Whatever is returned will be passed back to the user mapping provider module's
<code>__init__</code> method during construction.</li>
</ul>
</li>
<li><code>get_remote_user_id(self, userinfo)</code>
<ul>
<li>Arguments:
<ul>
<li><code>userinfo</code> - A <code>authlib.oidc.core.claims.UserInfo</code> object to extract user
information from.</li>
</ul>
</li>
<li>This method must return a string, which is the unique, immutable identifier
for the user. Commonly the <code>sub</code> claim of the response.</li>
</ul>
</li>
<li><code>map_user_attributes(self, userinfo, token, failures)</code>
<ul>
<li>This method must be async.</li>
<li>Arguments:
<ul>
<li><code>userinfo</code> - A <code>authlib.oidc.core.claims.UserInfo</code> object to extract user
information from.</li>
<li><code>token</code> - A dictionary which includes information necessary to make
further requests to the OpenID provider.</li>
<li><code>failures</code> - An <code>int</code> that represents the amount of times the returned
mxid localpart mapping has failed. This should be used
to create a deduplicated mxid localpart which should be
returned instead. For example, if this method returns
<code>john.doe</code> as the value of <code>localpart</code> in the returned
dict, and that is already taken on the homeserver, this
method will be called again with the same parameters but
with failures=1. The method should then return a different
<code>localpart</code> value, such as <code>john.doe1</code>.</li>
</ul>
</li>
<li>Returns a dictionary with two keys:
<ul>
<li><code>localpart</code>: A string, used to generate the Matrix ID. If this is
<code>None</code>, the user is prompted to pick their own username. This is only used
during a user's first login. Once a localpart has been associated with a
remote user ID (see <code>get_remote_user_id</code>) it cannot be updated.</li>
<li><code>displayname</code>: An optional string, the display name for the user.</li>
</ul>
</li>
</ul>
</li>
<li><code>get_extra_attributes(self, userinfo, token)</code>
<ul>
<li>
<p>This method must be async.</p>
</li>
<li>
<p>Arguments:</p>
<ul>
<li><code>userinfo</code> - A <code>authlib.oidc.core.claims.UserInfo</code> object to extract user
information from.</li>
<li><code>token</code> - A dictionary which includes information necessary to make
further requests to the OpenID provider.</li>
</ul>
</li>
<li>
<p>Returns a dictionary that is suitable to be serialized to JSON. This
will be returned as part of the response during a successful login.</p>
<p>Note that care should be taken to not overwrite any of the parameters
usually returned as part of the <a href="https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-login">login response</a>.</p>
</li>
</ul>
</li>
</ul>
<h3 id="default-openid-mapping-provider"><a class="header" href="#default-openid-mapping-provider">Default OpenID Mapping Provider</a></h3>
<p>Synapse has a built-in OpenID mapping provider if a custom provider isn't
specified in the config. It is located at
<a href="https://github.com/matrix-org/synapse/blob/develop/synapse/handlers/oidc.py"><code>synapse.handlers.oidc.JinjaOidcMappingProvider</code></a>.</p>
<h2 id="saml-mapping-providers"><a class="header" href="#saml-mapping-providers">SAML Mapping Providers</a></h2>
<p>The SAML mapping provider can be customized by editing the
<code>saml2_config.user_mapping_provider.module</code> config option.</p>
<p><code>saml2_config.user_mapping_provider.config</code> allows you to provide custom
configuration options to the module. Check with the module's documentation for
what options it provides (if any). The options listed by default are for the
user mapping provider built in to Synapse. If using a custom module, you should
comment these options out and use those specified by the module instead.</p>
<h3 id="building-a-custom-saml-mapping-provider"><a class="header" href="#building-a-custom-saml-mapping-provider">Building a Custom SAML Mapping Provider</a></h3>
<p>A custom mapping provider must specify the following methods:</p>
<ul>
<li><code>__init__(self, parsed_config, module_api)</code>
<ul>
<li>Arguments:
<ul>
<li><code>parsed_config</code> - A configuration object that is the return value of the
<code>parse_config</code> method. You should set any configuration options needed by
the module here.</li>
<li><code>module_api</code> - a <code>synapse.module_api.ModuleApi</code> object which provides the
stable API available for extension modules.</li>
</ul>
</li>
</ul>
</li>
<li><code>parse_config(config)</code>
<ul>
<li>This method should have the <code>@staticmethod</code> decoration.</li>
<li>Arguments:
<ul>
<li><code>config</code> - A <code>dict</code> representing the parsed content of the
<code>saml_config.user_mapping_provider.config</code> homeserver config option.
Runs on homeserver startup. Providers should extract and validate
any option values they need here.</li>
</ul>
</li>
<li>Whatever is returned will be passed back to the user mapping provider module's
<code>__init__</code> method during construction.</li>
</ul>
</li>
<li><code>get_saml_attributes(config)</code>
<ul>
<li>This method should have the <code>@staticmethod</code> decoration.</li>
<li>Arguments:
<ul>
<li><code>config</code> - A object resulting from a call to <code>parse_config</code>.</li>
</ul>
</li>
<li>Returns a tuple of two sets. The first set equates to the SAML auth
response attributes that are required for the module to function, whereas
the second set consists of those attributes which can be used if available,
but are not necessary.</li>
</ul>
</li>
<li><code>get_remote_user_id(self, saml_response, client_redirect_url)</code>
<ul>
<li>Arguments:
<ul>
<li><code>saml_response</code> - A <code>saml2.response.AuthnResponse</code> object to extract user
information from.</li>
<li><code>client_redirect_url</code> - A string, the URL that the client will be
redirected to.</li>
</ul>
</li>
<li>This method must return a string, which is the unique, immutable identifier
for the user. Commonly the <code>uid</code> claim of the response.</li>
</ul>
</li>
<li><code>saml_response_to_user_attributes(self, saml_response, failures, client_redirect_url)</code>
<ul>
<li>
<p>Arguments:</p>
<ul>
<li><code>saml_response</code> - A <code>saml2.response.AuthnResponse</code> object to extract user
information from.</li>
<li><code>failures</code> - An <code>int</code> that represents the amount of times the returned
mxid localpart mapping has failed. This should be used
to create a deduplicated mxid localpart which should be
returned instead. For example, if this method returns
<code>john.doe</code> as the value of <code>mxid_localpart</code> in the returned
dict, and that is already taken on the homeserver, this
method will be called again with the same parameters but
with failures=1. The method should then return a different
<code>mxid_localpart</code> value, such as <code>john.doe1</code>.</li>
<li><code>client_redirect_url</code> - A string, the URL that the client will be
redirected to.</li>
</ul>
</li>
<li>
<p>This method must return a dictionary, which will then be used by Synapse
to build a new user. The following keys are allowed:</p>
<ul>
<li><code>mxid_localpart</code> - A string, the mxid localpart of the new user. If this is
<code>None</code>, the user is prompted to pick their own username. This is only used
during a user's first login. Once a localpart has been associated with a
remote user ID (see <code>get_remote_user_id</code>) it cannot be updated.</li>
<li><code>displayname</code> - The displayname of the new user. If not provided, will default to
the value of <code>mxid_localpart</code>.</li>
<li><code>emails</code> - A list of emails for the new user. If not provided, will
default to an empty list.</li>
</ul>
<p>Alternatively it can raise a <code>synapse.api.errors.RedirectException</code> to
redirect the user to another page. This is useful to prompt the user for
additional information, e.g. if you want them to provide their own username.
It is the responsibility of the mapping provider to either redirect back
to <code>client_redirect_url</code> (including any additional information) or to
complete registration using methods from the <code>ModuleApi</code>.</p>
</li>
</ul>
</li>
</ul>
<h3 id="default-saml-mapping-provider"><a class="header" href="#default-saml-mapping-provider">Default SAML Mapping Provider</a></h3>
<p>Synapse has a built-in SAML mapping provider if a custom provider isn't
specified in the config. It is located at
<a href="https://github.com/matrix-org/synapse/blob/develop/synapse/handlers/saml.py"><code>synapse.handlers.saml.DefaultSamlMappingProvider</code></a>.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="password-auth-provider-modules"><a class="header" href="#password-auth-provider-modules">Password auth provider modules</a></h1>
<p>Password auth providers offer a way for server administrators to
integrate their Synapse installation with an existing authentication
system.</p>
<p>A password auth provider is a Python class which is dynamically loaded
into Synapse, and provides a number of methods by which it can integrate
with the authentication system.</p>
<p>This document serves as a reference for those looking to implement their
own password auth providers. Additionally, here is a list of known
password auth provider module implementations:</p>
<ul>
<li><a href="https://github.com/matrix-org/matrix-synapse-ldap3/">matrix-synapse-ldap3</a></li>
<li><a href="https://github.com/devture/matrix-synapse-shared-secret-auth">matrix-synapse-shared-secret-auth</a></li>
<li><a href="https://github.com/ma1uta/matrix-synapse-rest-password-provider">matrix-synapse-rest-password-provider</a></li>
</ul>
<h2 id="required-methods"><a class="header" href="#required-methods">Required methods</a></h2>
<p>Password auth provider classes must provide the following methods:</p>
<ul>
<li>
<p><code>parse_config(config)</code>
This method is passed the <code>config</code> object for this module from the
homeserver configuration file.</p>
<p>It should perform any appropriate sanity checks on the provided
configuration, and return an object which is then passed into
<code>__init__</code>.</p>
<p>This method should have the <code>@staticmethod</code> decoration.</p>
</li>
<li>
<p><code>__init__(self, config, account_handler)</code></p>
<p>The constructor is passed the config object returned by
<code>parse_config</code>, and a <code>synapse.module_api.ModuleApi</code> object which
allows the password provider to check if accounts exist and/or create
new ones.</p>
</li>
</ul>
<h2 id="optional-methods"><a class="header" href="#optional-methods">Optional methods</a></h2>
<p>Password auth provider classes may optionally provide the following methods:</p>
<ul>
<li>
<p><code>get_db_schema_files(self)</code></p>
<p>This method, if implemented, should return an Iterable of
<code>(name, stream)</code> pairs of database schema files. Each file is applied
in turn at initialisation, and a record is then made in the database
so that it is not re-applied on the next start.</p>
</li>
<li>
<p><code>get_supported_login_types(self)</code></p>
<p>This method, if implemented, should return a <code>dict</code> mapping from a
login type identifier (such as <code>m.login.password</code>) to an iterable
giving the fields which must be provided by the user in the submission
to <a href="https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-login">the <code>/login</code> API</a>.
These fields are passed in the <code>login_dict</code> dictionary to <code>check_auth</code>.</p>
<p>For example, if a password auth provider wants to implement a custom
login type of <code>com.example.custom_login</code>, where the client is expected
to pass the fields <code>secret1</code> and <code>secret2</code>, the provider should
implement this method and return the following dict:</p>
<pre><code class="language-python">{"com.example.custom_login": ("secret1", "secret2")}
</code></pre>
</li>
<li>
<p><code>check_auth(self, username, login_type, login_dict)</code></p>
<p>This method does the real work. If implemented, it
will be called for each login attempt where the login type matches one
of the keys returned by <code>get_supported_login_types</code>.</p>
<p>It is passed the (possibly unqualified) <code>user</code> field provided by the client,
the login type, and a dictionary of login secrets passed by the
client.</p>
<p>The method should return an <code>Awaitable</code> object, which resolves
to the canonical <code>@localpart:domain</code> user ID if authentication is
successful, and <code>None</code> if not.</p>
<p>Alternatively, the <code>Awaitable</code> can resolve to a <code>(str, func)</code> tuple, in
which case the second field is a callback which will be called with
the result from the <code>/login</code> call (including <code>access_token</code>,
<code>device_id</code>, etc.)</p>
</li>
<li>
<p><code>check_3pid_auth(self, medium, address, password)</code></p>
<p>This method, if implemented, is called when a user attempts to
register or log in with a third party identifier, such as email. It is
passed the medium (ex. "email"), an address (ex.
"<a href="mailto:jdoe@example.com">jdoe@example.com</a>") and the user's password.</p>
<p>The method should return an <code>Awaitable</code> object, which resolves
to a <code>str</code> containing the user's (canonical) User id if
authentication was successful, and <code>None</code> if not.</p>
<p>As with <code>check_auth</code>, the <code>Awaitable</code> may alternatively resolve to a
<code>(user_id, callback)</code> tuple.</p>
</li>
<li>
<p><code>check_password(self, user_id, password)</code></p>
<p>This method provides a simpler interface than
<code>get_supported_login_types</code> and <code>check_auth</code> for password auth
providers that just want to provide a mechanism for validating
<code>m.login.password</code> logins.</p>
<p>If implemented, it will be called to check logins with an
<code>m.login.password</code> login type. It is passed a qualified
<code>@localpart:domain</code> user id, and the password provided by the user.</p>
<p>The method should return an <code>Awaitable</code> object, which resolves
to <code>True</code> if authentication is successful, and <code>False</code> if not.</p>
</li>
<li>
<p><code>on_logged_out(self, user_id, device_id, access_token)</code></p>
<p>This method, if implemented, is called when a user logs out. It is
passed the qualified user ID, the ID of the deactivated device (if
any: access tokens are occasionally created without an associated
device ID), and the (now deactivated) access token.</p>
<p>It may return an <code>Awaitable</code> object; the logout request will
wait for the <code>Awaitable</code> to complete, but the result is ignored.</p>
</li>
</ul>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="jwt-login-type"><a class="header" href="#jwt-login-type">JWT Login Type</a></h1>
<p>Synapse comes with a non-standard login type to support
<a href="https://en.wikipedia.org/wiki/JSON_Web_Token">JSON Web Tokens</a>. In general the
documentation for
<a href="https://matrix.org/docs/spec/client_server/r0.6.1#login">the login endpoint</a>
is still valid (and the mechanism works similarly to the
<a href="https://matrix.org/docs/spec/client_server/r0.6.1#token-based">token based login</a>).</p>
<p>To log in using a JSON Web Token, clients should submit a <code>/login</code> request as
follows:</p>
<pre><code class="language-json">{
"type": "org.matrix.login.jwt",
"token": "<jwt>"
}
</code></pre>
<p>Note that the login type of <code>m.login.jwt</code> is supported, but is deprecated. This
will be removed in a future version of Synapse.</p>
<p>The <code>token</code> field should include the JSON web token with the following claims:</p>
<ul>
<li>The <code>sub</code> (subject) claim is required and should encode the local part of the
user ID.</li>
<li>The expiration time (<code>exp</code>), not before time (<code>nbf</code>), and issued at (<code>iat</code>)
claims are optional, but validated if present.</li>
<li>The issuer (<code>iss</code>) claim is optional, but required and validated if configured.</li>
<li>The audience (<code>aud</code>) claim is optional, but required and validated if configured.
Providing the audience claim when not configured will cause validation to fail.</li>
</ul>
<p>In the case that the token is not valid, the homeserver must respond with
<code>403 Forbidden</code> and an error code of <code>M_FORBIDDEN</code>.</p>
<p>As with other login types, there are additional fields (e.g. <code>device_id</code> and
<code>initial_device_display_name</code>) which can be included in the above request.</p>
<h2 id="preparing-synapse-1"><a class="header" href="#preparing-synapse-1">Preparing Synapse</a></h2>
<p>The JSON Web Token integration in Synapse uses the
<a href="https://pypi.org/project/pyjwt/"><code>PyJWT</code></a> library, which must be installed
as follows:</p>
<ul>
<li>
<p>The relevant libraries are included in the Docker images and Debian packages
provided by <code>matrix.org</code> so no further action is needed.</p>
</li>
<li>
<p>If you installed Synapse into a virtualenv, run <code>/path/to/env/bin/pip install synapse[pyjwt]</code> to install the necessary dependencies.</p>
</li>
<li>
<p>For other installation mechanisms, see the documentation provided by the
maintainer.</p>
</li>
</ul>
<p>To enable the JSON web token integration, you should then add an <code>jwt_config</code> section
to your configuration file (or uncomment the <code>enabled: true</code> line in the
existing section). See <a href="./sample_config.yaml">sample_config.yaml</a> for some
sample settings.</p>
<h2 id="how-to-test-jwt-as-a-developer"><a class="header" href="#how-to-test-jwt-as-a-developer">How to test JWT as a developer</a></h2>
<p>Although JSON Web Tokens are typically generated from an external server, the
examples below use <a href="https://pyjwt.readthedocs.io/en/latest/">PyJWT</a> directly.</p>
<ol>
<li>
<p>Configure Synapse with JWT logins, note that this example uses a pre-shared
secret and an algorithm of HS256:</p>
<pre><code class="language-yaml">jwt_config:
enabled: true
secret: "my-secret-token"
algorithm: "HS256"
</code></pre>
</li>
<li>
<p>Generate a JSON web token:</p>
<pre><code class="language-bash">$ pyjwt --key=my-secret-token --alg=HS256 encode sub=test-user
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXVzZXIifQ.Ag71GT8v01UO3w80aqRPTeuVPBIBZkYhNTJJ-_-zQIc
</code></pre>
</li>
<li>
<p>Query for the login types and ensure <code>org.matrix.login.jwt</code> is there:</p>
<pre><code class="language-bash">curl http://localhost:8080/_matrix/client/r0/login
</code></pre>
</li>
<li>
<p>Login used the generated JSON web token from above:</p>
<pre><code class="language-bash">$ curl http://localhost:8082/_matrix/client/r0/login -X POST \
--data '{"type":"org.matrix.login.jwt","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0LXVzZXIifQ.Ag71GT8v01UO3w80aqRPTeuVPBIBZkYhNTJJ-_-zQIc"}'
{
"access_token": "<access token>",
"device_id": "ACBDEFGHI",
"home_server": "localhost:8080",
"user_id": "@test-user:localhost:8480"
}
</code></pre>
</li>
</ol>
<p>You should now be able to use the returned access token to query the client API.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="overview-2"><a class="header" href="#overview-2">Overview</a></h1>
<p>A captcha can be enabled on your homeserver to help prevent bots from registering
accounts. Synapse currently uses Google's reCAPTCHA service which requires API keys
from Google.</p>
<h2 id="getting-api-keys"><a class="header" href="#getting-api-keys">Getting API keys</a></h2>
<ol>
<li>Create a new site at <a href="https://www.google.com/recaptcha/admin/create">https://www.google.com/recaptcha/admin/create</a></li>
<li>Set the label to anything you want</li>
<li>Set the type to reCAPTCHA v2 using the "I'm not a robot" Checkbox option.
This is the only type of captcha that works with Synapse.</li>
<li>Add the public hostname for your server, as set in <code>public_baseurl</code>
in <code>homeserver.yaml</code>, to the list of authorized domains. If you have not set
<code>public_baseurl</code>, use <code>server_name</code>.</li>
<li>Agree to the terms of service and submit.</li>
<li>Copy your site key and secret key and add them to your <code>homeserver.yaml</code>
configuration file
<pre><code>recaptcha_public_key: YOUR_SITE_KEY
recaptcha_private_key: YOUR_SECRET_KEY
</code></pre>
</li>
<li>Enable the CAPTCHA for new registrations
<pre><code>enable_registration_captcha: true
</code></pre>
</li>
<li>Go to the settings page for the CAPTCHA you just created</li>
<li>Uncheck the "Verify the origin of reCAPTCHA solutions" checkbox so that the
captcha can be displayed in any client. If you do not disable this option then you
must specify the domains of every client that is allowed to display the CAPTCHA.</li>
</ol>
<h2 id="configuring-ip-used-for-auth"><a class="header" href="#configuring-ip-used-for-auth">Configuring IP used for auth</a></h2>
<p>The reCAPTCHA API requires that the IP address of the user who solved the
CAPTCHA is sent. If the client is connecting through a proxy or load balancer,
it may be required to use the <code>X-Forwarded-For</code> (XFF) header instead of the origin
IP address. This can be configured using the <code>x_forwarded</code> directive in the
listeners section of the <code>homeserver.yaml</code> configuration file.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="registering-an-application-service"><a class="header" href="#registering-an-application-service">Registering an Application Service</a></h1>
<p>The registration of new application services depends on the homeserver used.
In synapse, you need to create a new configuration file for your AS and add it
to the list specified under the <code>app_service_config_files</code> config
option in your synapse config.</p>
<p>For example:</p>
<pre><code class="language-yaml">app_service_config_files:
- /home/matrix/.synapse/<your-AS>.yaml
</code></pre>
<p>The format of the AS configuration file is as follows:</p>
<pre><code class="language-yaml">url: <base url of AS>
as_token: <token AS will add to requests to HS>
hs_token: <token HS will add to requests to AS>
sender_localpart: <localpart of AS user>
namespaces:
users: # List of users we're interested in
- exclusive: <bool>
regex: <regex>
group_id: <group>
- ...
aliases: [] # List of aliases we're interested in
rooms: [] # List of room ids we're interested in
</code></pre>
<p><code>exclusive</code>: If enabled, only this application service is allowed to register users in its namespace(s).
<code>group_id</code>: All users of this application service are dynamically joined to this group. This is useful for e.g user organisation or flairs.</p>
<p>See the <a href="https://matrix.org/docs/spec/application_service/unstable.html">spec</a> for further details on how application services work.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="server-notices"><a class="header" href="#server-notices">Server Notices</a></h1>
<p>'Server Notices' are a new feature introduced in Synapse 0.30. They provide a
channel whereby server administrators can send messages to users on the server.</p>
<p>They are used as part of communication of the server polices (see
<a href="consent_tracking.html">Consent Tracking</a>), however the intention is that
they may also find a use for features such as "Message of the day".</p>
<p>This is a feature specific to Synapse, but it uses standard Matrix
communication mechanisms, so should work with any Matrix client.</p>
<h2 id="user-experience"><a class="header" href="#user-experience">User experience</a></h2>
<p>When the user is first sent a server notice, they will get an invitation to a
room (typically called 'Server Notices', though this is configurable in
<code>homeserver.yaml</code>). They will be <strong>unable to reject</strong> this invitation -
attempts to do so will receive an error.</p>
<p>Once they accept the invitation, they will see the notice message in the room
history; it will appear to have come from the 'server notices user' (see
below).</p>
<p>The user is prevented from sending any messages in this room by the power
levels.</p>
<p>Having joined the room, the user can leave the room if they want. Subsequent
server notices will then cause a new room to be created.</p>
<h2 id="synapse-configuration"><a class="header" href="#synapse-configuration">Synapse configuration</a></h2>
<p>Server notices come from a specific user id on the server. Server
administrators are free to choose the user id - something like <code>server</code> is
suggested, meaning the notices will come from
<code>@server:<your_server_name></code>. Once the Server Notices user is configured, that
user id becomes a special, privileged user, so administrators should ensure
that <strong>it is not already allocated</strong>.</p>
<p>In order to support server notices, it is necessary to add some configuration
to the <code>homeserver.yaml</code> file. In particular, you should add a <code>server_notices</code>
section, which should look like this:</p>
<pre><code class="language-yaml">server_notices:
system_mxid_localpart: server
system_mxid_display_name: "Server Notices"
system_mxid_avatar_url: "mxc://server.com/oumMVlgDnLYFaPVkExemNVVZ"
room_name: "Server Notices"
</code></pre>
<p>The only compulsory setting is <code>system_mxid_localpart</code>, which defines the user
id of the Server Notices user, as above. <code>room_name</code> defines the name of the
room which will be created.</p>
<p><code>system_mxid_display_name</code> and <code>system_mxid_avatar_url</code> can be used to set the
displayname and avatar of the Server Notices user.</p>
<h2 id="sending-notices"><a class="header" href="#sending-notices">Sending notices</a></h2>
<p>To send server notices to users you can use the
<a href="admin_api/server_notices.html">admin_api</a>.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="support-in-synapse-for-tracking-agreement-to-server-terms-and-conditions"><a class="header" href="#support-in-synapse-for-tracking-agreement-to-server-terms-and-conditions">Support in Synapse for tracking agreement to server terms and conditions</a></h1>
<p>Synapse 0.30 introduces support for tracking whether users have agreed to the
terms and conditions set by the administrator of a server - and blocking access
to the server until they have.</p>
<p>There are several parts to this functionality; each requires some specific
configuration in <code>homeserver.yaml</code> to be enabled.</p>
<p>Note that various parts of the configuation and this document refer to the
"privacy policy": agreement with a privacy policy is one particular use of this
feature, but of course adminstrators can specify other terms and conditions
unrelated to "privacy" per se.</p>
<h2 id="collecting-policy-agreement-from-a-user"><a class="header" href="#collecting-policy-agreement-from-a-user">Collecting policy agreement from a user</a></h2>
<p>Synapse can be configured to serve the user a simple policy form with an
"accept" button. Clicking "Accept" records the user's acceptance in the
database and shows a success page.</p>
<p>To enable this, first create templates for the policy and success pages.
These should be stored on the local filesystem.</p>
<p>These templates use the <a href="http://jinja.pocoo.org">Jinja2</a> templating language,
and <a href="https://github.com/matrix-org/synapse/tree/develop/docs/privacy_policy_templates/">docs/privacy_policy_templates</a>
gives examples of the sort of thing that can be done.</p>
<p>Note that the templates must be stored under a name giving the language of the
template - currently this must always be <code>en</code> (for "English");
internationalisation support is intended for the future.</p>
<p>The template for the policy itself should be versioned and named according to
the version: for example <code>1.0.html</code>. The version of the policy which the user
has agreed to is stored in the database.</p>
<p>Once the templates are in place, make the following changes to <code>homeserver.yaml</code>:</p>
<ol>
<li>
<p>Add a <code>user_consent</code> section, which should look like:</p>
<pre><code class="language-yaml">user_consent:
template_dir: privacy_policy_templates
version: 1.0
</code></pre>
<p><code>template_dir</code> points to the directory containing the policy
templates. <code>version</code> defines the version of the policy which will be served
to the user. In the example above, Synapse will serve
<code>privacy_policy_templates/en/1.0.html</code>.</p>
</li>
<li>
<p>Add a <code>form_secret</code> setting at the top level:</p>
<pre><code class="language-yaml">form_secret: "<unique secret>"
</code></pre>
<p>This should be set to an arbitrary secret string (try <code>pwgen -y 30</code> to
generate suitable secrets).</p>
<p>More on what this is used for below.</p>
</li>
<li>
<p>Add <code>consent</code> wherever the <code>client</code> resource is currently enabled in the
<code>listeners</code> configuration. For example:</p>
<pre><code class="language-yaml">listeners:
- port: 8008
resources:
- names:
- client
- consent
</code></pre>
</li>
</ol>
<p>Finally, ensure that <code>jinja2</code> is installed. If you are using a virtualenv, this
should be a matter of <code>pip install Jinja2</code>. On debian, try <code>apt-get install python-jinja2</code>.</p>
<p>Once this is complete, and the server has been restarted, try visiting
<code>https://<server>/_matrix/consent</code>. If correctly configured, this should give
an error "Missing string query parameter 'u'". It is now possible to manually
construct URIs where users can give their consent.</p>
<h3 id="enabling-consent-tracking-at-registration"><a class="header" href="#enabling-consent-tracking-at-registration">Enabling consent tracking at registration</a></h3>
<ol>
<li>
<p>Add the following to your configuration:</p>
<pre><code class="language-yaml">user_consent:
require_at_registration: true
policy_name: "Privacy Policy" # or whatever you'd like to call the policy
</code></pre>
</li>
<li>
<p>In your consent templates, make use of the <code>public_version</code> variable to
see if an unauthenticated user is viewing the page. This is typically
wrapped around the form that would be used to actually agree to the document:</p>
<pre><code>{% if not public_version %}
<!-- The variables used here are only provided when the 'u' param is given to the homeserver -->
<form method="post" action="consent">
<input type="hidden" name="v" value="{{version}}"/>
<input type="hidden" name="u" value="{{user}}"/>
<input type="hidden" name="h" value="{{userhmac}}"/>
<input type="submit" value="Sure thing!"/>
</form>
{% endif %}
</code></pre>
</li>
<li>
<p>Restart Synapse to apply the changes.</p>
</li>
</ol>
<p>Visiting <code>https://<server>/_matrix/consent</code> should now give you a view of the privacy
document. This is what users will be able to see when registering for accounts.</p>
<h3 id="constructing-the-consent-uri"><a class="header" href="#constructing-the-consent-uri">Constructing the consent URI</a></h3>
<p>It may be useful to manually construct the "consent URI" for a given user - for
instance, in order to send them an email asking them to consent. To do this,
take the base <code>https://<server>/_matrix/consent</code> URL and add the following
query parameters:</p>
<ul>
<li>
<p><code>u</code>: the user id of the user. This can either be a full MXID
(<code>@user:server.com</code>) or just the localpart (<code>user</code>).</p>
</li>
<li>
<p><code>h</code>: hex-encoded HMAC-SHA256 of <code>u</code> using the <code>form_secret</code> as a key. It is
possible to calculate this on the commandline with something like:</p>
<pre><code class="language-bash">echo -n '<user>' | openssl sha256 -hmac '<form_secret>'
</code></pre>
<p>This should result in a URI which looks something like:
<code>https://<server>/_matrix/consent?u=<user>&h=68a152465a4d...</code>.</p>
</li>
</ul>
<p>Note that not providing a <code>u</code> parameter will be interpreted as wanting to view
the document from an unauthenticated perspective, such as prior to registration.
Therefore, the <code>h</code> parameter is not required in this scenario. To enable this
behaviour, set <code>require_at_registration</code> to <code>true</code> in your <code>user_consent</code> config.</p>
<h2 id="sending-users-a-server-notice-asking-them-to-agree-to-the-policy"><a class="header" href="#sending-users-a-server-notice-asking-them-to-agree-to-the-policy">Sending users a server notice asking them to agree to the policy</a></h2>
<p>It is possible to configure Synapse to send a <a href="server_notices.html">server
notice</a> to anybody who has not yet agreed to the current
version of the policy. To do so:</p>
<ul>
<li>
<p>ensure that the consent resource is configured, as in the previous section</p>
</li>
<li>
<p>ensure that server notices are configured, as in <a href="server_notices.html">the server notice documentation</a>.</p>
</li>
<li>
<p>Add <code>server_notice_content</code> under <code>user_consent</code> in <code>homeserver.yaml</code>. For
example:</p>
<pre><code class="language-yaml">user_consent:
server_notice_content:
msgtype: m.text
body: >-
Please give your consent to the privacy policy at %(consent_uri)s.
</code></pre>
<p>Synapse automatically replaces the placeholder <code>%(consent_uri)s</code> with the
consent uri for that user.</p>
</li>
<li>
<p>ensure that <code>public_baseurl</code> is set in <code>homeserver.yaml</code>, and gives the base
URI that clients use to connect to the server. (It is used to construct
<code>consent_uri</code> in the server notice.)</p>
</li>
</ul>
<h2 id="blocking-users-from-using-the-server-until-they-agree-to-the-policy"><a class="header" href="#blocking-users-from-using-the-server-until-they-agree-to-the-policy">Blocking users from using the server until they agree to the policy</a></h2>
<p>Synapse can be configured to block any attempts to join rooms or send messages
until the user has given their agreement to the policy. (Joining the server
notices room is exempted from this).</p>
<p>To enable this, add <code>block_events_error</code> under <code>user_consent</code>. For example:</p>
<pre><code class="language-yaml">user_consent:
block_events_error: >-
You can't send any messages until you consent to the privacy policy at
%(consent_uri)s.
</code></pre>
<p>Synapse automatically replaces the placeholder <code>%(consent_uri)s</code> with the
consent uri for that user.</p>
<p>ensure that <code>public_baseurl</code> is set in <code>homeserver.yaml</code>, and gives the base
URI that clients use to connect to the server. (It is used to construct
<code>consent_uri</code> in the error.)</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="url-previews-1"><a class="header" href="#url-previews-1">URL Previews</a></h1>
<p>Design notes on a URL previewing service for Matrix:</p>
<p>Options are:</p>
<ol>
<li>Have an AS which listens for URLs, downloads them, and inserts an event that describes their metadata.</li>
</ol>
<ul>
<li>Pros:
<ul>
<li>Decouples the implementation entirely from Synapse.</li>
<li>Uses existing Matrix events & content repo to store the metadata.</li>
</ul>
</li>
<li>Cons:
<ul>
<li>Which AS should provide this service for a room, and why should you trust it?</li>
<li>Doesn't work well with E2E; you'd have to cut the AS into every room</li>
<li>the AS would end up subscribing to every room anyway.</li>
</ul>
</li>
</ul>
<ol start="2">
<li>Have a generic preview API (nothing to do with Matrix) that provides a previewing service:</li>
</ol>
<ul>
<li>Pros:
<ul>
<li>Simple and flexible; can be used by any clients at any point</li>
</ul>
</li>
<li>Cons:
<ul>
<li>If each HS provides one of these independently, all the HSes in a room may needlessly DoS the target URI</li>
<li>We need somewhere to store the URL metadata rather than just using Matrix itself</li>
<li>We can't piggyback on matrix to distribute the metadata between HSes.</li>
</ul>
</li>
</ul>
<ol start="3">
<li>Make the synapse of the sending user responsible for spidering the URL and inserting an event asynchronously which describes the metadata.</li>
</ol>
<ul>
<li>Pros:
<ul>
<li>Works transparently for all clients</li>
<li>Piggy-backs nicely on using Matrix for distributing the metadata.</li>
<li>No confusion as to which AS</li>
</ul>
</li>
<li>Cons:
<ul>
<li>Doesn't work with E2E</li>
<li>We might want to decouple the implementation of the spider from the HS, given spider behaviour can be quite complicated and evolve much more rapidly than the HS. It's more like a bot than a core part of the server.</li>
</ul>
</li>
</ul>
<ol start="4">
<li>Make the sending client use the preview API and insert the event itself when successful.</li>
</ol>
<ul>
<li>Pros:
<ul>
<li>Works well with E2E</li>
<li>No custom server functionality</li>
<li>Lets the client customise the preview that they send (like on FB)</li>
</ul>
</li>
<li>Cons:
<ul>
<li>Entirely specific to the sending client, whereas it'd be nice if /any/ URL was correctly previewed if clients support it.</li>
</ul>
</li>
</ul>
<ol start="5">
<li>Have the option of specifying a shared (centralised) previewing service used by a room, to avoid all the different HSes in the room DoSing the target.</li>
</ol>
<p>Best solution is probably a combination of both 2 and 4.</p>
<ul>
<li>Sending clients do their best to create and send a preview at the point of sending the message, perhaps delaying the message until the preview is computed? (This also lets the user validate the preview before sending)</li>
<li>Receiving clients have the option of going and creating their own preview if one doesn't arrive soon enough (or if the original sender didn't create one)</li>
</ul>
<p>This is a bit magical though in that the preview could come from two entirely different sources - the sending HS or your local one. However, this can always be exposed to users: "Generate your own URL previews if none are available?"</p>
<p>This is tantamount also to senders calculating their own thumbnails for sending in advance of the main content - we are trusting the sender not to lie about the content in the thumbnail. Whereas currently thumbnails are calculated by the receiving homeserver to avoid this attack.</p>
<p>However, this kind of phishing attack does exist whether we let senders pick their thumbnails or not, in that a malicious sender can send normal text messages around the attachment claiming it to be legitimate. We could rely on (future) reputation/abuse management to punish users who phish (be it with bogus metadata or bogus descriptions). Bogus metadata is particularly bad though, especially if it's avoidable.</p>
<p>As a first cut, let's do #2 and have the receiver hit the API to calculate its own previews (as it does currently for image thumbnails). We can then extend/optimise this to option 4 as a special extra if needed.</p>
<h2 id="api"><a class="header" href="#api">API</a></h2>
<pre><code>GET /_matrix/media/r0/preview_url?url=http://wherever.com
200 OK
{
"og:type" : "article"
"og:url" : "https://twitter.com/matrixdotorg/status/684074366691356672"
"og:title" : "Matrix on Twitter"
"og:image" : "https://pbs.twimg.com/profile_images/500400952029888512/yI0qtFi7_400x400.png"
"og:description" : "“Synapse 0.12 is out! Lots of polishing, performance &amp;amp; bugfixes: /sync API, /r0 prefix, fulltext search, 3PID invites https://t.co/5alhXLLEGP”"
"og:site_name" : "Twitter"
}
</code></pre>
<ul>
<li>Downloads the URL
<ul>
<li>If HTML, just stores it in RAM and parses it for OG meta tags
<ul>
<li>Download any media OG meta tags to the media repo, and refer to them in the OG via mxc:// URIs.</li>
</ul>
</li>
<li>If a media filetype we know we can thumbnail: store it on disk, and hand it to the thumbnailer. Generate OG meta tags from the thumbnailer contents.</li>
<li>Otherwise, don't bother downloading further.</li>
</ul>
</li>
</ul>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="user-directory-api-implementation"><a class="header" href="#user-directory-api-implementation">User Directory API Implementation</a></h1>
<p>The user directory is currently maintained based on the 'visible' users
on this particular server - i.e. ones which your account shares a room with, or
who are present in a publicly viewable room present on the server.</p>
<p>The directory info is stored in various tables, which can (typically after
DB corruption) get stale or out of sync. If this happens, for now the
solution to fix it is to execute the SQL <a href="https://github.com/matrix-org/synapse/blob/master/synapse/storage/schema/main/delta/53/user_dir_populate.sql">here</a>
and then restart synapse. This should then start a background task to
flush the current tables and regenerate the directory.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="message-retention-policies"><a class="header" href="#message-retention-policies">Message retention policies</a></h1>
<p>Synapse admins can enable support for message retention policies on
their homeserver. Message retention policies exist at a room level,
follow the semantics described in
<a href="https://github.com/matrix-org/matrix-doc/blob/matthew/msc1763/proposals/1763-configurable-retention-periods.md">MSC1763</a>,
and allow server and room admins to configure how long messages should
be kept in a homeserver's database before being purged from it.
<strong>Please note that, as this feature isn't part of the Matrix
specification yet, this implementation is to be considered as
experimental.</strong> </p>
<p>A message retention policy is mainly defined by its <code>max_lifetime</code>
parameter, which defines how long a message can be kept around after
it was sent to the room. If a room doesn't have a message retention
policy, and there's no default one for a given server, then no message
sent in that room is ever purged on that server.</p>
<p>MSC1763 also specifies semantics for a <code>min_lifetime</code> parameter which
defines the amount of time after which an event <em>can</em> get purged (after
it was sent to the room), but Synapse doesn't currently support it
beyond registering it.</p>
<p>Both <code>max_lifetime</code> and <code>min_lifetime</code> are optional parameters.</p>
<p>Note that message retention policies don't apply to state events.</p>
<p>Once an event reaches its expiry date (defined as the time it was sent
plus the value for <code>max_lifetime</code> in the room), two things happen:</p>
<ul>
<li>Synapse stops serving the event to clients via any endpoint.</li>
<li>The message gets picked up by the next purge job (see the "Purge jobs"
section) and is removed from Synapse's database.</li>
</ul>
<p>Since purge jobs don't run continuously, this means that an event might
stay in a server's database for longer than the value for <code>max_lifetime</code>
in the room would allow, though hidden from clients.</p>
<p>Similarly, if a server (with support for message retention policies
enabled) receives from another server an event that should have been
purged according to its room's policy, then the receiving server will
process and store that event until it's picked up by the next purge job,
though it will always hide it from clients.</p>
<p>Synapse requires at least one message in each room, so it will never
delete the last message in a room. It will, however, hide it from
clients.</p>
<h2 id="server-configuration"><a class="header" href="#server-configuration">Server configuration</a></h2>
<p>Support for this feature can be enabled and configured in the
<code>retention</code> section of the Synapse configuration file (see the
<a href="https://github.com/matrix-org/synapse/blob/v1.36.0/docs/sample_config.yaml#L451-L518">sample file</a>).</p>
<p>To enable support for message retention policies, set the setting
<code>enabled</code> in this section to <code>true</code>.</p>
<h3 id="default-policy"><a class="header" href="#default-policy">Default policy</a></h3>
<p>A default message retention policy is a policy defined in Synapse's
configuration that is used by Synapse for every room that doesn't have a
message retention policy configured in its state. This allows server
admins to ensure that messages are never kept indefinitely in a server's
database. </p>
<p>A default policy can be defined as such, in the <code>retention</code> section of
the configuration file:</p>
<pre><code class="language-yaml"> default_policy:
min_lifetime: 1d
max_lifetime: 1y
</code></pre>
<p>Here, <code>min_lifetime</code> and <code>max_lifetime</code> have the same meaning and level
of support as previously described. They can be expressed either as a
duration (using the units <code>s</code> (seconds), <code>m</code> (minutes), <code>h</code> (hours),
<code>d</code> (days), <code>w</code> (weeks) and <code>y</code> (years)) or as a number of milliseconds.</p>
<h3 id="purge-jobs"><a class="header" href="#purge-jobs">Purge jobs</a></h3>
<p>Purge jobs are the jobs that Synapse runs in the background to purge
expired events from the database. They are only run if support for
message retention policies is enabled in the server's configuration. If
no configuration for purge jobs is configured by the server admin,
Synapse will use a default configuration, which is described in the
<a href="https://github.com/matrix-org/synapse/blob/v1.36.0/docs/sample_config.yaml#L451-L518">sample configuration file</a>.</p>
<p>Some server admins might want a finer control on when events are removed
depending on an event's room's policy. This can be done by setting the
<code>purge_jobs</code> sub-section in the <code>retention</code> section of the configuration
file. An example of such configuration could be:</p>
<pre><code class="language-yaml"> purge_jobs:
- longest_max_lifetime: 3d
interval: 12h
- shortest_max_lifetime: 3d
longest_max_lifetime: 1w
interval: 1d
- shortest_max_lifetime: 1w
interval: 2d
</code></pre>
<p>In this example, we define three jobs:</p>
<ul>
<li>one that runs twice a day (every 12 hours) and purges events in rooms
which policy's <code>max_lifetime</code> is lower or equal to 3 days.</li>
<li>one that runs once a day and purges events in rooms which policy's
<code>max_lifetime</code> is between 3 days and a week.</li>
<li>one that runs once every 2 days and purges events in rooms which
policy's <code>max_lifetime</code> is greater than a week.</li>
</ul>
<p>Note that this example is tailored to show different configurations and
features slightly more jobs than it's probably necessary (in practice, a
server admin would probably consider it better to replace the two last
jobs with one that runs once a day and handles rooms which which
policy's <code>max_lifetime</code> is greater than 3 days).</p>
<p>Keep in mind, when configuring these jobs, that a purge job can become
quite heavy on the server if it targets many rooms, therefore prefer
having jobs with a low interval that target a limited set of rooms. Also
make sure to include a job with no minimum and one with no maximum to
make sure your configuration handles every policy.</p>
<p>As previously mentioned in this documentation, while a purge job that
runs e.g. every day means that an expired event might stay in the
database for up to a day after its expiry, Synapse hides expired events
from clients as soon as they expire, so the event is not visible to
local users between its expiry date and the moment it gets purged from
the server's database.</p>
<h3 id="lifetime-limits"><a class="header" href="#lifetime-limits">Lifetime limits</a></h3>
<p>Server admins can set limits on the values of <code>max_lifetime</code> to use when
purging old events in a room. These limits can be defined as such in the
<code>retention</code> section of the configuration file:</p>
<pre><code class="language-yaml"> allowed_lifetime_min: 1d
allowed_lifetime_max: 1y
</code></pre>
<p>The limits are considered when running purge jobs. If necessary, the
effective value of <code>max_lifetime</code> will be brought between
<code>allowed_lifetime_min</code> and <code>allowed_lifetime_max</code> (inclusive).
This means that, if the value of <code>max_lifetime</code> defined in the room's state
is lower than <code>allowed_lifetime_min</code>, the value of <code>allowed_lifetime_min</code>
will be used instead. Likewise, if the value of <code>max_lifetime</code> is higher
than <code>allowed_lifetime_max</code>, the value of <code>allowed_lifetime_max</code> will be
used instead.</p>
<p>In the example above, we ensure Synapse never deletes events that are less
than one day old, and that it always deletes events that are over a year
old.</p>
<p>If a default policy is set, and its <code>max_lifetime</code> value is lower than
<code>allowed_lifetime_min</code> or higher than <code>allowed_lifetime_max</code>, the same
process applies.</p>
<p>Both parameters are optional; if one is omitted Synapse won't use it to
adjust the effective value of <code>max_lifetime</code>.</p>
<p>Like other settings in this section, these parameters can be expressed
either as a duration or as a number of milliseconds.</p>
<h2 id="room-configuration"><a class="header" href="#room-configuration">Room configuration</a></h2>
<p>To configure a room's message retention policy, a room's admin or
moderator needs to send a state event in that room with the type
<code>m.room.retention</code> and the following content:</p>
<pre><code class="language-json">{
"max_lifetime": ...
}
</code></pre>
<p>In this event's content, the <code>max_lifetime</code> parameter has the same
meaning as previously described, and needs to be expressed in
milliseconds. The event's content can also include a <code>min_lifetime</code>
parameter, which has the same meaning and limited support as previously
described.</p>
<p>Note that over every server in the room, only the ones with support for
message retention policies will actually remove expired events. This
support is currently not enabled by default in Synapse.</p>
<h2 id="note-on-reclaiming-disk-space"><a class="header" href="#note-on-reclaiming-disk-space">Note on reclaiming disk space</a></h2>
<p>While purge jobs actually delete data from the database, the disk space
used by the database might not decrease immediately on the database's
host. However, even though the database engine won't free up the disk
space, it will start writing new data into where the purged data was.</p>
<p>If you want to reclaim the freed disk space anyway and return it to the
operating system, the server admin needs to run <code>VACUUM FULL;</code> (or
<code>VACUUM;</code> for SQLite databases) on Synapse's database (see the related
<a href="https://www.postgresql.org/docs/current/sql-vacuum.html">PostgreSQL documentation</a>).</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="modules"><a class="header" href="#modules">Modules</a></h1>
<p>Synapse supports extending its functionality by configuring external modules.</p>
<h2 id="using-modules"><a class="header" href="#using-modules">Using modules</a></h2>
<p>To use a module on Synapse, add it to the <code>modules</code> section of the configuration file:</p>
<pre><code class="language-yaml">modules:
- module: my_super_module.MySuperClass
config:
do_thing: true
- module: my_other_super_module.SomeClass
config: {}
</code></pre>
<p>Each module is defined by a path to a Python class as well as a configuration. This
information for a given module should be available in the module's own documentation.</p>
<p><strong>Note</strong>: When using third-party modules, you effectively allow someone else to run
custom code on your Synapse homeserver. Server admins are encouraged to verify the
provenance of the modules they use on their homeserver and make sure the modules aren't
running malicious code on their instance.</p>
<p>Also note that we are currently in the process of migrating module interfaces to this
system. While some interfaces might be compatible with it, others still require
configuring modules in another part of Synapse's configuration file. Currently, only the
spam checker interface is compatible with this new system.</p>
<h2 id="writing-a-module"><a class="header" href="#writing-a-module">Writing a module</a></h2>
<p>A module is a Python class that uses Synapse's module API to interact with the
homeserver. It can register callbacks that Synapse will call on specific operations, as
well as web resources to attach to Synapse's web server.</p>
<p>When instantiated, a module is given its parsed configuration as well as an instance of
the <code>synapse.module_api.ModuleApi</code> class. The configuration is a dictionary, and is
either the output of the module's <code>parse_config</code> static method (see below), or the
configuration associated with the module in Synapse's configuration file.</p>
<p>See the documentation for the <code>ModuleApi</code> class
<a href="https://github.com/matrix-org/synapse/blob/master/synapse/module_api/__init__.py">here</a>.</p>
<h3 id="handling-the-modules-configuration"><a class="header" href="#handling-the-modules-configuration">Handling the module's configuration</a></h3>
<p>A module can implement the following static method:</p>
<pre><code class="language-python">@staticmethod
def parse_config(config: dict) -> dict
</code></pre>
<p>This method is given a dictionary resulting from parsing the YAML configuration for the
module. It may modify it (for example by parsing durations expressed as strings (e.g.
"5d") into milliseconds, etc.), and return the modified dictionary. It may also verify
that the configuration is correct, and raise an instance of
<code>synapse.module_api.errors.ConfigError</code> if not.</p>
<h3 id="registering-a-web-resource"><a class="header" href="#registering-a-web-resource">Registering a web resource</a></h3>
<p>Modules can register web resources onto Synapse's web server using the following module
API method:</p>
<pre><code class="language-python">def ModuleApi.register_web_resource(path: str, resource: IResource) -> None
</code></pre>
<p>The path is the full absolute path to register the resource at. For example, if you
register a resource for the path <code>/_synapse/client/my_super_module/say_hello</code>, Synapse
will serve it at <code>http(s)://[HS_URL]/_synapse/client/my_super_module/say_hello</code>. Note
that Synapse does not allow registering resources for several sub-paths in the <code>/_matrix</code>
namespace (such as anything under <code>/_matrix/client</code> for example). It is strongly
recommended that modules register their web resources under the <code>/_synapse/client</code>
namespace.</p>
<p>The provided resource is a Python class that implements Twisted's <a href="https://twistedmatrix.com/documents/current/api/twisted.web.resource.IResource.html">IResource</a>
interface (such as <a href="https://twistedmatrix.com/documents/current/api/twisted.web.resource.Resource.html">Resource</a>).</p>
<p>Only one resource can be registered for a given path. If several modules attempt to
register a resource for the same path, the module that appears first in Synapse's
configuration file takes priority.</p>
<p>Modules <strong>must</strong> register their web resources in their <code>__init__</code> method.</p>
<h3 id="registering-a-callback"><a class="header" href="#registering-a-callback">Registering a callback</a></h3>
<p>Modules can use Synapse's module API to register callbacks. Callbacks are functions that
Synapse will call when performing specific actions. Callbacks must be asynchronous, and
are split in categories. A single module may implement callbacks from multiple categories,
and is under no obligation to implement all callbacks from the categories it registers
callbacks for.</p>
<p>Modules can register callbacks using one of the module API's <code>register_[...]_callbacks</code>
methods. The callback functions are passed to these methods as keyword arguments, with
the callback name as the argument name and the function as its value. This is demonstrated
in the example below. A <code>register_[...]_callbacks</code> method exists for each module type
documented in this section.</p>
<h4 id="spam-checker-callbacks"><a class="header" href="#spam-checker-callbacks">Spam checker callbacks</a></h4>
<p>Spam checker callbacks allow module developers to implement spam mitigation actions for
Synapse instances. Spam checker callbacks can be registered using the module API's
<code>register_spam_checker_callbacks</code> method.</p>
<p>The available spam checker callbacks are:</p>
<pre><code class="language-python">async def check_event_for_spam(event: "synapse.events.EventBase") -> Union[bool, str]
</code></pre>
<p>Called when receiving an event from a client or via federation. The module can return
either a <code>bool</code> to indicate whether the event must be rejected because of spam, or a <code>str</code>
to indicate the event must be rejected because of spam and to give a rejection reason to
forward to clients.</p>
<pre><code class="language-python">async def user_may_invite(inviter: str, invitee: str, room_id: str) -> bool
</code></pre>
<p>Called when processing an invitation. The module must return a <code>bool</code> indicating whether
the inviter can invite the invitee to the given room. Both inviter and invitee are
represented by their Matrix user ID (e.g. <code>@alice:example.com</code>).</p>
<pre><code class="language-python">async def user_may_create_room(user: str) -> bool
</code></pre>
<p>Called when processing a room creation request. The module must return a <code>bool</code> indicating
whether the given user (represented by their Matrix user ID) is allowed to create a room.</p>
<pre><code class="language-python">async def user_may_create_room_alias(user: str, room_alias: "synapse.types.RoomAlias") -> bool
</code></pre>
<p>Called when trying to associate an alias with an existing room. The module must return a
<code>bool</code> indicating whether the given user (represented by their Matrix user ID) is allowed
to set the given alias.</p>
<pre><code class="language-python">async def user_may_publish_room(user: str, room_id: str) -> bool
</code></pre>
<p>Called when trying to publish a room to the homeserver's public rooms directory. The
module must return a <code>bool</code> indicating whether the given user (represented by their
Matrix user ID) is allowed to publish the given room.</p>
<pre><code class="language-python">async def check_username_for_spam(user_profile: Dict[str, str]) -> bool
</code></pre>
<p>Called when computing search results in the user directory. The module must return a
<code>bool</code> indicating whether the given user profile can appear in search results. The profile
is represented as a dictionary with the following keys:</p>
<ul>
<li><code>user_id</code>: The Matrix ID for this user.</li>
<li><code>display_name</code>: The user's display name.</li>
<li><code>avatar_url</code>: The <code>mxc://</code> URL to the user's avatar.</li>
</ul>
<p>The module is given a copy of the original dictionary, so modifying it from within the
module cannot modify a user's profile when included in user directory search results.</p>
<pre><code class="language-python">async def check_registration_for_spam(
email_threepid: Optional[dict],
username: Optional[str],
request_info: Collection[Tuple[str, str]],
auth_provider_id: Optional[str] = None,
) -> "synapse.spam_checker_api.RegistrationBehaviour"
</code></pre>
<p>Called when registering a new user. The module must return a <code>RegistrationBehaviour</code>
indicating whether the registration can go through or must be denied, or whether the user
may be allowed to register but will be shadow banned.</p>
<p>The arguments passed to this callback are:</p>
<ul>
<li><code>email_threepid</code>: The email address used for registering, if any.</li>
<li><code>username</code>: The username the user would like to register. Can be <code>None</code>, meaning that
Synapse will generate one later.</li>
<li><code>request_info</code>: A collection of tuples, which first item is a user agent, and which
second item is an IP address. These user agents and IP addresses are the ones that were
used during the registration process.</li>
<li><code>auth_provider_id</code>: The identifier of the SSO authentication provider, if any.</li>
</ul>
<pre><code class="language-python">async def check_media_file_for_spam(
file_wrapper: "synapse.rest.media.v1.media_storage.ReadableFileWrapper",
file_info: "synapse.rest.media.v1._base.FileInfo",
) -> bool
</code></pre>
<p>Called when storing a local or remote file. The module must return a boolean indicating
whether the given file can be stored in the homeserver's media store.</p>
<h4 id="account-validity-callbacks"><a class="header" href="#account-validity-callbacks">Account validity callbacks</a></h4>
<p>Account validity callbacks allow module developers to add extra steps to verify the
validity on an account, i.e. see if a user can be granted access to their account on the
Synapse instance. Account validity callbacks can be registered using the module API's
<code>register_account_validity_callbacks</code> method.</p>
<p>The available account validity callbacks are:</p>
<pre><code class="language-python">async def is_user_expired(user: str) -> Optional[bool]
</code></pre>
<p>Called when processing any authenticated request (except for logout requests). The module
can return a <code>bool</code> to indicate whether the user has expired and should be locked out of
their account, or <code>None</code> if the module wasn't able to figure it out. The user is
represented by their Matrix user ID (e.g. <code>@alice:example.com</code>).</p>
<p>If the module returns <code>True</code>, the current request will be denied with the error code
<code>ORG_MATRIX_EXPIRED_ACCOUNT</code> and the HTTP status code 403. Note that this doesn't
invalidate the user's access token.</p>
<pre><code class="language-python">async def on_user_registration(user: str) -> None
</code></pre>
<p>Called after successfully registering a user, in case the module needs to perform extra
operations to keep track of them. (e.g. add them to a database table). The user is
represented by their Matrix user ID.</p>
<h4 id="third-party-rules-callbacks"><a class="header" href="#third-party-rules-callbacks">Third party rules callbacks</a></h4>
<p>Third party rules callbacks allow module developers to add extra checks to verify the
validity of incoming events. Third party event rules callbacks can be registered using
the module API's <code>register_third_party_rules_callbacks</code> method.</p>
<p>The available third party rules callbacks are:</p>
<pre><code class="language-python">async def check_event_allowed(
event: "synapse.events.EventBase",
state_events: "synapse.types.StateMap",
) -> Tuple[bool, Optional[dict]]
</code></pre>
<p><strong><span style="color:red">
This callback is very experimental and can and will break without notice. Module developers
are encouraged to implement <code>check_event_for_spam</code> from the spam checker category instead.
</span></strong></p>
<p>Called when processing any incoming event, with the event and a <code>StateMap</code>
representing the current state of the room the event is being sent into. A <code>StateMap</code> is
a dictionary that maps tuples containing an event type and a state key to the
corresponding state event. For example retrieving the room's <code>m.room.create</code> event from
the <code>state_events</code> argument would look like this: <code>state_events.get(("m.room.create", ""))</code>.
The module must return a boolean indicating whether the event can be allowed.</p>
<p>Note that this callback function processes incoming events coming via federation
traffic (on top of client traffic). This means denying an event might cause the local
copy of the room's history to diverge from that of remote servers. This may cause
federation issues in the room. It is strongly recommended to only deny events using this
callback function if the sender is a local user, or in a private federation in which all
servers are using the same module, with the same configuration.</p>
<p>If the boolean returned by the module is <code>True</code>, it may also tell Synapse to replace the
event with new data by returning the new event's data as a dictionary. In order to do
that, it is recommended the module calls <code>event.get_dict()</code> to get the current event as a
dictionary, and modify the returned dictionary accordingly.</p>
<p>Note that replacing the event only works for events sent by local users, not for events
received over federation.</p>
<pre><code class="language-python">async def on_create_room(
requester: "synapse.types.Requester",
request_content: dict,
is_requester_admin: bool,
) -> None
</code></pre>
<p>Called when processing a room creation request, with the <code>Requester</code> object for the user
performing the request, a dictionary representing the room creation request's JSON body
(see <a href="https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-createroom">the spec</a>
for a list of possible parameters), and a boolean indicating whether the user performing
the request is a server admin.</p>
<p>Modules can modify the <code>request_content</code> (by e.g. adding events to its <code>initial_state</code>),
or deny the room's creation by raising a <code>module_api.errors.SynapseError</code>.</p>
<h3 id="porting-an-existing-module-that-uses-the-old-interface"><a class="header" href="#porting-an-existing-module-that-uses-the-old-interface">Porting an existing module that uses the old interface</a></h3>
<p>In order to port a module that uses Synapse's old module interface, its author needs to:</p>
<ul>
<li>ensure the module's callbacks are all asynchronous.</li>
<li>register their callbacks using one or more of the <code>register_[...]_callbacks</code> methods
from the <code>ModuleApi</code> class in the module's <code>__init__</code> method (see <a href="modules.html#registering-a-callback">this section</a>
for more info).</li>
</ul>
<p>Additionally, if the module is packaged with an additional web resource, the module
should register this resource in its <code>__init__</code> method using the <code>register_web_resource</code>
method from the <code>ModuleApi</code> class (see <a href="modules.html#registering-a-web-resource">this section</a> for
more info).</p>
<p>The module's author should also update any example in the module's configuration to only
use the new <code>modules</code> section in Synapse's configuration file (see <a href="modules.html#using-modules">this section</a>
for more info).</p>
<h3 id="example"><a class="header" href="#example">Example</a></h3>
<p>The example below is a module that implements the spam checker callback
<code>user_may_create_room</code> to deny room creation to user <code>@evilguy:example.com</code>, and registers
a web resource to the path <code>/_synapse/client/demo/hello</code> that returns a JSON object.</p>
<pre><code class="language-python">import json
from twisted.web.resource import Resource
from twisted.web.server import Request
from synapse.module_api import ModuleApi
class DemoResource(Resource):
def __init__(self, config):
super(DemoResource, self).__init__()
self.config = config
def render_GET(self, request: Request):
name = request.args.get(b"name")[0]
request.setHeader(b"Content-Type", b"application/json")
return json.dumps({"hello": name})
class DemoModule:
def __init__(self, config: dict, api: ModuleApi):
self.config = config
self.api = api
self.api.register_web_resource(
path="/_synapse/client/demo/hello",
resource=DemoResource(self.config),
)
self.api.register_spam_checker_callbacks(
user_may_create_room=self.user_may_create_room,
)
@staticmethod
def parse_config(config):
return config
async def user_may_create_room(self, user: str) -> bool:
if user == "@evilguy:example.com":
return False
return True
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h2 style="color:red">
This page of the Synapse documentation is now deprecated. For up to date
documentation on setting up or writing a spam checker module, please see
<a href="modules.html">this page</a>.
</h2>
<h1 id="handling-spam-in-synapse"><a class="header" href="#handling-spam-in-synapse">Handling spam in Synapse</a></h1>
<p>Synapse has support to customize spam checking behavior. It can plug into a
variety of events and affect how they are presented to users on your homeserver.</p>
<p>The spam checking behavior is implemented as a Python class, which must be
able to be imported by the running Synapse.</p>
<h2 id="python-spam-checker-class"><a class="header" href="#python-spam-checker-class">Python spam checker class</a></h2>
<p>The Python class is instantiated with two objects:</p>
<ul>
<li>Any configuration (see below).</li>
<li>An instance of <code>synapse.module_api.ModuleApi</code>.</li>
</ul>
<p>It then implements methods which return a boolean to alter behavior in Synapse.
All the methods must be defined.</p>
<p>There's a generic method for checking every event (<code>check_event_for_spam</code>), as
well as some specific methods:</p>
<ul>
<li><code>user_may_invite</code></li>
<li><code>user_may_create_room</code></li>
<li><code>user_may_create_room_alias</code></li>
<li><code>user_may_publish_room</code></li>
<li><code>check_username_for_spam</code></li>
<li><code>check_registration_for_spam</code></li>
<li><code>check_media_file_for_spam</code></li>
</ul>
<p>The details of each of these methods (as well as their inputs and outputs)
are documented in the <code>synapse.events.spamcheck.SpamChecker</code> class.</p>
<p>The <code>ModuleApi</code> class provides a way for the custom spam checker class to
call back into the homeserver internals.</p>
<p>Additionally, a <code>parse_config</code> method is mandatory and receives the plugin config
dictionary. After parsing, It must return an object which will be
passed to <code>__init__</code> later.</p>
<h3 id="example-1"><a class="header" href="#example-1">Example</a></h3>
<pre><code class="language-python">from synapse.spam_checker_api import RegistrationBehaviour
class ExampleSpamChecker:
def __init__(self, config, api):
self.config = config
self.api = api
@staticmethod
def parse_config(config):
return config
async def check_event_for_spam(self, foo):
return False # allow all events
async def user_may_invite(self, inviter_userid, invitee_userid, room_id):
return True # allow all invites
async def user_may_create_room(self, userid):
return True # allow all room creations
async def user_may_create_room_alias(self, userid, room_alias):
return True # allow all room aliases
async def user_may_publish_room(self, userid, room_id):
return True # allow publishing of all rooms
async def check_username_for_spam(self, user_profile):
return False # allow all usernames
async def check_registration_for_spam(
self,
email_threepid,
username,
request_info,
auth_provider_id,
):
return RegistrationBehaviour.ALLOW # allow all registrations
async def check_media_file_for_spam(self, file_wrapper, file_info):
return False # allow all media
</code></pre>
<h2 id="configuration-2"><a class="header" href="#configuration-2">Configuration</a></h2>
<p>Modify the <code>spam_checker</code> section of your <code>homeserver.yaml</code> in the following
manner:</p>
<p>Create a list entry with the keys <code>module</code> and <code>config</code>.</p>
<ul>
<li>
<p><code>module</code> should point to the fully qualified Python class that implements your
custom logic, e.g. <code>my_module.ExampleSpamChecker</code>.</p>
</li>
<li>
<p><code>config</code> is a dictionary that gets passed to the spam checker class.</p>
</li>
</ul>
<h3 id="example-2"><a class="header" href="#example-2">Example</a></h3>
<p>This section might look like:</p>
<pre><code class="language-yaml">spam_checker:
- module: my_module.ExampleSpamChecker
config:
# Enable or disable a specific option in ExampleSpamChecker.
my_custom_option: true
</code></pre>
<p>More spam checkers can be added in tandem by appending more items to the list. An
action is blocked when at least one of the configured spam checkers flags it.</p>
<h2 id="examples"><a class="header" href="#examples">Examples</a></h2>
<p>The <a href="https://github.com/matrix-org/mjolnir">Mjolnir</a> project is a full fledged
example using the Synapse spam checking API, including a bot for dynamic
configuration.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="presence-router-module"><a class="header" href="#presence-router-module">Presence Router Module</a></h1>
<p>Synapse supports configuring a module that can specify additional users
(local or remote) to should receive certain presence updates from local
users.</p>
<p>Note that routing presence via Application Service transactions is not
currently supported.</p>
<p>The presence routing module is implemented as a Python class, which will
be imported by the running Synapse.</p>
<h2 id="python-presence-router-class"><a class="header" href="#python-presence-router-class">Python Presence Router Class</a></h2>
<p>The Python class is instantiated with two objects:</p>
<ul>
<li>A configuration object of some type (see below).</li>
<li>An instance of <code>synapse.module_api.ModuleApi</code>.</li>
</ul>
<p>It then implements methods related to presence routing.</p>
<p>Note that one method of <code>ModuleApi</code> that may be useful is:</p>
<pre><code class="language-python">async def ModuleApi.send_local_online_presence_to(users: Iterable[str]) -> None
</code></pre>
<p>which can be given a list of local or remote MXIDs to broadcast known, online user
presence to (for those users that the receiving user is considered interested in).
It does not include state for users who are currently offline, and it can only be
called on workers that support sending federation. Additionally, this method must
only be called from the process that has been configured to write to the
the <a href="workers.html#stream-writers">presence stream</a>.
By default, this is the main process, but another worker can be configured to do
so.</p>
<h3 id="module-structure"><a class="header" href="#module-structure">Module structure</a></h3>
<p>Below is a list of possible methods that can be implemented, and whether they are
required.</p>
<h4 id="parse_config"><a class="header" href="#parse_config"><code>parse_config</code></a></h4>
<pre><code class="language-python">def parse_config(config_dict: dict) -> Any
</code></pre>
<p><strong>Required.</strong> A static method that is passed a dictionary of config options, and
should return a validated config object. This method is described further in
<a href="presence_router_module.html#configuration">Configuration</a>.</p>
<h4 id="get_users_for_states"><a class="header" href="#get_users_for_states"><code>get_users_for_states</code></a></h4>
<pre><code class="language-python">async def get_users_for_states(
self,
state_updates: Iterable[UserPresenceState],
) -> Dict[str, Set[UserPresenceState]]:
</code></pre>
<p><strong>Required.</strong> An asynchronous method that is passed an iterable of user presence
state. This method can determine whether a given presence update should be sent to certain
users. It does this by returning a dictionary with keys representing local or remote
Matrix User IDs, and values being a python set
of <code>synapse.handlers.presence.UserPresenceState</code> instances.</p>
<p>Synapse will then attempt to send the specified presence updates to each user when
possible.</p>
<h4 id="get_interested_users"><a class="header" href="#get_interested_users"><code>get_interested_users</code></a></h4>
<pre><code class="language-python">async def get_interested_users(self, user_id: str) -> Union[Set[str], str]
</code></pre>
<p><strong>Required.</strong> An asynchronous method that is passed a single Matrix User ID. This
method is expected to return the users that the passed in user may be interested in the
presence of. Returned users may be local or remote. The presence routed as a result of
what this method returns is sent in addition to the updates already sent between users
that share a room together. Presence updates are deduplicated.</p>
<p>This method should return a python set of Matrix User IDs, or the object
<code>synapse.events.presence_router.PresenceRouter.ALL_USERS</code> to indicate that the passed
user should receive presence information for <em>all</em> known users.</p>
<p>For clarity, if the user <code>@alice:example.org</code> is passed to this method, and the Set
<code>{"@bob:example.com", "@charlie:somewhere.org"}</code> is returned, this signifies that Alice
should receive presence updates sent by Bob and Charlie, regardless of whether these
users share a room.</p>
<h3 id="example-3"><a class="header" href="#example-3">Example</a></h3>
<p>Below is an example implementation of a presence router class.</p>
<pre><code class="language-python">from typing import Dict, Iterable, Set, Union
from synapse.events.presence_router import PresenceRouter
from synapse.handlers.presence import UserPresenceState
from synapse.module_api import ModuleApi
class PresenceRouterConfig:
def __init__(self):
# Config options with their defaults
# A list of users to always send all user presence updates to
self.always_send_to_users = [] # type: List[str]
# A list of users to ignore presence updates for. Does not affect
# shared-room presence relationships
self.blacklisted_users = [] # type: List[str]
class ExamplePresenceRouter:
"""An example implementation of synapse.presence_router.PresenceRouter.
Supports routing all presence to a configured set of users, or a subset
of presence from certain users to members of certain rooms.
Args:
config: A configuration object.
module_api: An instance of Synapse's ModuleApi.
"""
def __init__(self, config: PresenceRouterConfig, module_api: ModuleApi):
self._config = config
self._module_api = module_api
@staticmethod
def parse_config(config_dict: dict) -> PresenceRouterConfig:
"""Parse a configuration dictionary from the homeserver config, do
some validation and return a typed PresenceRouterConfig.
Args:
config_dict: The configuration dictionary.
Returns:
A validated config object.
"""
# Initialise a typed config object
config = PresenceRouterConfig()
always_send_to_users = config_dict.get("always_send_to_users")
blacklisted_users = config_dict.get("blacklisted_users")
# Do some validation of config options... otherwise raise a
# synapse.config.ConfigError.
config.always_send_to_users = always_send_to_users
config.blacklisted_users = blacklisted_users
return config
async def get_users_for_states(
self,
state_updates: Iterable[UserPresenceState],
) -> Dict[str, Set[UserPresenceState]]:
"""Given an iterable of user presence updates, determine where each one
needs to go. Returned results will not affect presence updates that are
sent between users who share a room.
Args:
state_updates: An iterable of user presence state updates.
Returns:
A dictionary of user_id -> set of UserPresenceState that the user should
receive.
"""
destination_users = {} # type: Dict[str, Set[UserPresenceState]
# Ignore any updates for blacklisted users
desired_updates = set()
for update in state_updates:
if update.state_key not in self._config.blacklisted_users:
desired_updates.add(update)
# Send all presence updates to specific users
for user_id in self._config.always_send_to_users:
destination_users[user_id] = desired_updates
return destination_users
async def get_interested_users(
self,
user_id: str,
) -> Union[Set[str], PresenceRouter.ALL_USERS]:
"""
Retrieve a list of users that `user_id` is interested in receiving the
presence of. This will be in addition to those they share a room with.
Optionally, the object PresenceRouter.ALL_USERS can be returned to indicate
that this user should receive all incoming local and remote presence updates.
Note that this method will only be called for local users.
Args:
user_id: A user requesting presence updates.
Returns:
A set of user IDs to return additional presence updates for, or
PresenceRouter.ALL_USERS to return presence updates for all other users.
"""
if user_id in self._config.always_send_to_users:
return PresenceRouter.ALL_USERS
return set()
</code></pre>
<h4 id="a-note-on-get_users_for_states-and-get_interested_users"><a class="header" href="#a-note-on-get_users_for_states-and-get_interested_users">A note on <code>get_users_for_states</code> and <code>get_interested_users</code></a></h4>
<p>Both of these methods are effectively two different sides of the same coin. The logic
regarding which users should receive updates for other users should be the same
between them.</p>
<p><code>get_users_for_states</code> is called when presence updates come in from either federation
or local users, and is used to either direct local presence to remote users, or to
wake up the sync streams of local users to collect remote presence.</p>
<p>In contrast, <code>get_interested_users</code> is used to determine the users that presence should
be fetched for when a local user is syncing. This presence is then retrieved, before
being fed through <code>get_users_for_states</code> once again, with only the syncing user's
routing information pulled from the resulting dictionary.</p>
<p>Their routing logic should thus line up, else you may run into unintended behaviour.</p>
<h2 id="configuration-3"><a class="header" href="#configuration-3">Configuration</a></h2>
<p>Once you've crafted your module and installed it into the same Python environment as
Synapse, amend your homeserver config file with the following.</p>
<pre><code class="language-yaml">presence:
enabled: true
presence_router:
module: my_module.ExamplePresenceRouter
config:
# Any configuration options for your module. The below is an example.
# of setting options for ExamplePresenceRouter.
always_send_to_users: ["@presence_gobbler:example.org"]
blacklisted_users:
- "@alice:example.com"
- "@bob:example.com"
...
</code></pre>
<p>The contents of <code>config</code> will be passed as a Python dictionary to the static
<code>parse_config</code> method of your class. The object returned by this method will
then be passed to the <code>__init__</code> method of your module as <code>config</code>.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="scaling-synapse-via-workers"><a class="header" href="#scaling-synapse-via-workers">Scaling synapse via workers</a></h1>
<p>For small instances it recommended to run Synapse in the default monolith mode.
For larger instances where performance is a concern it can be helpful to split
out functionality into multiple separate python processes. These processes are
called 'workers', and are (eventually) intended to scale horizontally
independently.</p>
<p>Synapse's worker support is under active development and subject to change as
we attempt to rapidly scale ever larger Synapse instances. However we are
documenting it here to help admins needing a highly scalable Synapse instance
similar to the one running <code>matrix.org</code>.</p>
<p>All processes continue to share the same database instance, and as such,
workers only work with PostgreSQL-based Synapse deployments. SQLite should only
be used for demo purposes and any admin considering workers should already be
running PostgreSQL.</p>
<p>See also <a href="https://matrix.org/blog/2020/11/03/how-we-fixed-synapses-scalability">Matrix.org blog post</a>
for a higher level overview.</p>
<h2 id="main-processworker-communication"><a class="header" href="#main-processworker-communication">Main process/worker communication</a></h2>
<p>The processes communicate with each other via a Synapse-specific protocol called
'replication' (analogous to MySQL- or Postgres-style database replication) which
feeds streams of newly written data between processes so they can be kept in
sync with the database state.</p>
<p>When configured to do so, Synapse uses a
<a href="https://redis.io/topics/pubsub">Redis pub/sub channel</a> to send the replication
stream between all configured Synapse processes. Additionally, processes may
make HTTP requests to each other, primarily for operations which need to wait
for a reply ─ such as sending an event.</p>
<p>Redis support was added in v1.13.0 with it becoming the recommended method in
v1.18.0. It replaced the old direct TCP connections (which is deprecated as of
v1.18.0) to the main process. With Redis, rather than all the workers connecting
to the main process, all the workers and the main process connect to Redis,
which relays replication commands between processes. This can give a significant
cpu saving on the main process and will be a prerequisite for upcoming
performance improvements.</p>
<p>If Redis support is enabled Synapse will use it as a shared cache, as well as a
pub/sub mechanism.</p>
<p>See the <a href="workers.html#architectural-diagram">Architectural diagram</a> section at the end for
a visualisation of what this looks like.</p>
<h2 id="setting-up-workers"><a class="header" href="#setting-up-workers">Setting up workers</a></h2>
<p>A Redis server is required to manage the communication between the processes.
The Redis server should be installed following the normal procedure for your
distribution (e.g. <code>apt install redis-server</code> on Debian). It is safe to use an
existing Redis deployment if you have one.</p>
<p>Once installed, check that Redis is running and accessible from the host running
Synapse, for example by executing <code>echo PING | nc -q1 localhost 6379</code> and seeing
a response of <code>+PONG</code>.</p>
<p>The appropriate dependencies must also be installed for Synapse. If using a
virtualenv, these can be installed with:</p>
<pre><code class="language-sh">pip install "matrix-synapse[redis]"
</code></pre>
<p>Note that these dependencies are included when synapse is installed with <code>pip install matrix-synapse[all]</code>. They are also included in the debian packages from
<code>matrix.org</code> and in the docker images at
https://hub.docker.com/r/matrixdotorg/synapse/.</p>
<p>To make effective use of the workers, you will need to configure an HTTP
reverse-proxy such as nginx or haproxy, which will direct incoming requests to
the correct worker, or to the main synapse instance. See
<a href="reverse_proxy.html">the reverse proxy documentation</a> for information on setting up a reverse
proxy.</p>
<p>When using workers, each worker process has its own configuration file which
contains settings specific to that worker, such as the HTTP listener that it
provides (if any), logging configuration, etc.</p>
<p>Normally, the worker processes are configured to read from a shared
configuration file as well as the worker-specific configuration files. This
makes it easier to keep common configuration settings synchronised across all
the processes.</p>
<p>The main process is somewhat special in this respect: it does not normally
need its own configuration file and can take all of its configuration from the
shared configuration file.</p>
<h3 id="shared-configuration"><a class="header" href="#shared-configuration">Shared configuration</a></h3>
<p>Normally, only a couple of changes are needed to make an existing configuration
file suitable for use with workers. First, you need to enable an "HTTP replication
listener" for the main process; and secondly, you need to enable redis-based
replication. Optionally, a shared secret can be used to authenticate HTTP
traffic between workers. For example:</p>
<pre><code class="language-yaml"># extend the existing `listeners` section. This defines the ports that the
# main process will listen on.
listeners:
# The HTTP replication port
- port: 9093
bind_address: '127.0.0.1'
type: http
resources:
- names: [replication]
# Add a random shared secret to authenticate traffic.
worker_replication_secret: ""
redis:
enabled: true
</code></pre>
<p>See the sample config for the full documentation of each option.</p>
<p>Under <strong>no circumstances</strong> should the replication listener be exposed to the
public internet; it has no authentication and is unencrypted.</p>
<h3 id="worker-configuration"><a class="header" href="#worker-configuration">Worker configuration</a></h3>
<p>In the config file for each worker, you must specify the type of worker
application (<code>worker_app</code>), and you should specify a unique name for the worker
(<code>worker_name</code>). The currently available worker applications are listed below.
You must also specify the HTTP replication endpoint that it should talk to on
the main synapse process. <code>worker_replication_host</code> should specify the host of
the main synapse and <code>worker_replication_http_port</code> should point to the HTTP
replication port. If the worker will handle HTTP requests then the
<code>worker_listeners</code> option should be set with a <code>http</code> listener, in the same way
as the <code>listeners</code> option in the shared config.</p>
<p>For example:</p>
<pre><code class="language-yaml">worker_app: synapse.app.generic_worker
worker_name: worker1
# The replication listener on the main synapse process.
worker_replication_host: 127.0.0.1
worker_replication_http_port: 9093
worker_listeners:
- type: http
port: 8083
resources:
- names:
- client
- federation
worker_log_config: /home/matrix/synapse/config/worker1_log_config.yaml
</code></pre>
<p>...is a full configuration for a generic worker instance, which will expose a
plain HTTP endpoint on port 8083 separately serving various endpoints, e.g.
<code>/sync</code>, which are listed below.</p>
<p>Obviously you should configure your reverse-proxy to route the relevant
endpoints to the worker (<code>localhost:8083</code> in the above example).</p>
<h3 id="running-synapse-with-workers"><a class="header" href="#running-synapse-with-workers">Running Synapse with workers</a></h3>
<p>Finally, you need to start your worker processes. This can be done with either
<code>synctl</code> or your distribution's preferred service manager such as <code>systemd</code>. We
recommend the use of <code>systemd</code> where available: for information on setting up
<code>systemd</code> to start synapse workers, see
<a href="systemd-with-workers">Systemd with Workers</a>. To use <code>synctl</code>, see
<a href="synctl_workers.html">Using synctl with Workers</a>.</p>
<h2 id="available-worker-applications"><a class="header" href="#available-worker-applications">Available worker applications</a></h2>
<h3 id="synapseappgeneric_worker"><a class="header" href="#synapseappgeneric_worker"><code>synapse.app.generic_worker</code></a></h3>
<p>This worker can handle API requests matching the following regular
expressions:</p>
<pre><code># Sync requests
^/_matrix/client/(v2_alpha|r0)/sync$
^/_matrix/client/(api/v1|v2_alpha|r0)/events$
^/_matrix/client/(api/v1|r0)/initialSync$
^/_matrix/client/(api/v1|r0)/rooms/[^/]+/initialSync$
# Federation requests
^/_matrix/federation/v1/event/
^/_matrix/federation/v1/state/
^/_matrix/federation/v1/state_ids/
^/_matrix/federation/v1/backfill/
^/_matrix/federation/v1/get_missing_events/
^/_matrix/federation/v1/publicRooms
^/_matrix/federation/v1/query/
^/_matrix/federation/v1/make_join/
^/_matrix/federation/v1/make_leave/
^/_matrix/federation/v1/send_join/
^/_matrix/federation/v2/send_join/
^/_matrix/federation/v1/send_leave/
^/_matrix/federation/v2/send_leave/
^/_matrix/federation/v1/invite/
^/_matrix/federation/v2/invite/
^/_matrix/federation/v1/query_auth/
^/_matrix/federation/v1/event_auth/
^/_matrix/federation/v1/exchange_third_party_invite/
^/_matrix/federation/v1/user/devices/
^/_matrix/federation/v1/get_groups_publicised$
^/_matrix/key/v2/query
# Inbound federation transaction request
^/_matrix/federation/v1/send/
# Client API requests
^/_matrix/client/(api/v1|r0|unstable)/publicRooms$
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/joined_members$
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/context/.*$
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/members$
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/state$
^/_matrix/client/(api/v1|r0|unstable)/account/3pid$
^/_matrix/client/(api/v1|r0|unstable)/devices$
^/_matrix/client/(api/v1|r0|unstable)/keys/query$
^/_matrix/client/(api/v1|r0|unstable)/keys/changes$
^/_matrix/client/versions$
^/_matrix/client/(api/v1|r0|unstable)/voip/turnServer$
^/_matrix/client/(api/v1|r0|unstable)/joined_groups$
^/_matrix/client/(api/v1|r0|unstable)/publicised_groups$
^/_matrix/client/(api/v1|r0|unstable)/publicised_groups/
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/event/
^/_matrix/client/(api/v1|r0|unstable)/joined_rooms$
^/_matrix/client/(api/v1|r0|unstable)/search$
# Registration/login requests
^/_matrix/client/(api/v1|r0|unstable)/login$
^/_matrix/client/(r0|unstable)/register$
# Event sending requests
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/redact
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/send
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/state/
^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/(join|invite|leave|ban|unban|kick)$
^/_matrix/client/(api/v1|r0|unstable)/join/
^/_matrix/client/(api/v1|r0|unstable)/profile/
</code></pre>
<p>Additionally, the following REST endpoints can be handled for GET requests:</p>
<pre><code>^/_matrix/federation/v1/groups/
</code></pre>
<p>Pagination requests can also be handled, but all requests for a given
room must be routed to the same instance. Additionally, care must be taken to
ensure that the purge history admin API is not used while pagination requests
for the room are in flight:</p>
<pre><code>^/_matrix/client/(api/v1|r0|unstable)/rooms/.*/messages$
</code></pre>
<p>Additionally, the following endpoints should be included if Synapse is configured
to use SSO (you only need to include the ones for whichever SSO provider you're
using):</p>
<pre><code># for all SSO providers
^/_matrix/client/(api/v1|r0|unstable)/login/sso/redirect
^/_synapse/client/pick_idp$
^/_synapse/client/pick_username
^/_synapse/client/new_user_consent$
^/_synapse/client/sso_register$
# OpenID Connect requests.
^/_synapse/client/oidc/callback$
# SAML requests.
^/_synapse/client/saml2/authn_response$
# CAS requests.
^/_matrix/client/(api/v1|r0|unstable)/login/cas/ticket$
</code></pre>
<p>Ensure that all SSO logins go to a single process.
For multiple workers not handling the SSO endpoints properly, see
<a href="https://github.com/matrix-org/synapse/issues/7530">#7530</a> and
<a href="https://github.com/matrix-org/synapse/issues/9427">#9427</a>.</p>
<p>Note that a HTTP listener with <code>client</code> and <code>federation</code> resources must be
configured in the <code>worker_listeners</code> option in the worker config.</p>
<h4 id="load-balancing"><a class="header" href="#load-balancing">Load balancing</a></h4>
<p>It is possible to run multiple instances of this worker app, with incoming requests
being load-balanced between them by the reverse-proxy. However, different endpoints
have different characteristics and so admins
may wish to run multiple groups of workers handling different endpoints so that
load balancing can be done in different ways.</p>
<p>For <code>/sync</code> and <code>/initialSync</code> requests it will be more efficient if all
requests from a particular user are routed to a single instance. Extracting a
user ID from the access token or <code>Authorization</code> header is currently left as an
exercise for the reader. Admins may additionally wish to separate out <code>/sync</code>
requests that have a <code>since</code> query parameter from those that don't (and
<code>/initialSync</code>), as requests that don't are known as "initial sync" that happens
when a user logs in on a new device and can be <em>very</em> resource intensive, so
isolating these requests will stop them from interfering with other users ongoing
syncs.</p>
<p>Federation and client requests can be balanced via simple round robin.</p>
<p>The inbound federation transaction request <code>^/_matrix/federation/v1/send/</code>
should be balanced by source IP so that transactions from the same remote server
go to the same process.</p>
<p>Registration/login requests can be handled separately purely to help ensure that
unexpected load doesn't affect new logins and sign ups.</p>
<p>Finally, event sending requests can be balanced by the room ID in the URI (or
the full URI, or even just round robin), the room ID is the path component after
<code>/rooms/</code>. If there is a large bridge connected that is sending or may send lots
of events, then a dedicated set of workers can be provisioned to limit the
effects of bursts of events from that bridge on events sent by normal users.</p>
<h4 id="stream-writers"><a class="header" href="#stream-writers">Stream writers</a></h4>
<p>Additionally, there is <em>experimental</em> support for moving writing of specific
streams (such as events) off of the main process to a particular worker. (This
is only supported with Redis-based replication.)</p>
<p>Currently supported streams are <code>events</code> and <code>typing</code>.</p>
<p>To enable this, the worker must have a HTTP replication listener configured,
have a <code>worker_name</code> and be listed in the <code>instance_map</code> config. For example to
move event persistence off to a dedicated worker, the shared configuration would
include:</p>
<pre><code class="language-yaml">instance_map:
event_persister1:
host: localhost
port: 8034
stream_writers:
events: event_persister1
</code></pre>
<p>The <code>events</code> stream also experimentally supports having multiple writers, where
work is sharded between them by room ID. Note that you <em>must</em> restart all worker
instances when adding or removing event persisters. An example <code>stream_writers</code>
configuration with multiple writers:</p>
<pre><code class="language-yaml">stream_writers:
events:
- event_persister1
- event_persister2
</code></pre>
<h4 id="background-tasks"><a class="header" href="#background-tasks">Background tasks</a></h4>
<p>There is also <em>experimental</em> support for moving background tasks to a separate
worker. Background tasks are run periodically or started via replication. Exactly
which tasks are configured to run depends on your Synapse configuration (e.g. if
stats is enabled).</p>
<p>To enable this, the worker must have a <code>worker_name</code> and can be configured to run
background tasks. For example, to move background tasks to a dedicated worker,
the shared configuration would include:</p>
<pre><code class="language-yaml">run_background_tasks_on: background_worker
</code></pre>
<p>You might also wish to investigate the <code>update_user_directory</code> and
<code>media_instance_running_background_jobs</code> settings.</p>
<h3 id="synapseapppusher"><a class="header" href="#synapseapppusher"><code>synapse.app.pusher</code></a></h3>
<p>Handles sending push notifications to sygnal and email. Doesn't handle any
REST endpoints itself, but you should set <code>start_pushers: False</code> in the
shared configuration file to stop the main synapse sending push notifications.</p>
<p>To run multiple instances at once the <code>pusher_instances</code> option should list all
pusher instances by their worker name, e.g.:</p>
<pre><code class="language-yaml">pusher_instances:
- pusher_worker1
- pusher_worker2
</code></pre>
<h3 id="synapseappappservice"><a class="header" href="#synapseappappservice"><code>synapse.app.appservice</code></a></h3>
<p>Handles sending output traffic to Application Services. Doesn't handle any
REST endpoints itself, but you should set <code>notify_appservices: False</code> in the
shared configuration file to stop the main synapse sending appservice notifications.</p>
<p>Note this worker cannot be load-balanced: only one instance should be active.</p>
<h3 id="synapseappfederation_sender"><a class="header" href="#synapseappfederation_sender"><code>synapse.app.federation_sender</code></a></h3>
<p>Handles sending federation traffic to other servers. Doesn't handle any
REST endpoints itself, but you should set <code>send_federation: False</code> in the
shared configuration file to stop the main synapse sending this traffic.</p>
<p>If running multiple federation senders then you must list each
instance in the <code>federation_sender_instances</code> option by their <code>worker_name</code>.
All instances must be stopped and started when adding or removing instances.
For example:</p>
<pre><code class="language-yaml">federation_sender_instances:
- federation_sender1
- federation_sender2
</code></pre>
<h3 id="synapseappmedia_repository"><a class="header" href="#synapseappmedia_repository"><code>synapse.app.media_repository</code></a></h3>
<p>Handles the media repository. It can handle all endpoints starting with:</p>
<pre><code>/_matrix/media/
</code></pre>
<p>... and the following regular expressions matching media-specific administration APIs:</p>
<pre><code>^/_synapse/admin/v1/purge_media_cache$
^/_synapse/admin/v1/room/.*/media.*$
^/_synapse/admin/v1/user/.*/media.*$
^/_synapse/admin/v1/media/.*$
^/_synapse/admin/v1/quarantine_media/.*$
</code></pre>
<p>You should also set <code>enable_media_repo: False</code> in the shared configuration
file to stop the main synapse running background jobs related to managing the
media repository.</p>
<p>In the <code>media_repository</code> worker configuration file, configure the http listener to
expose the <code>media</code> resource. For example:</p>
<pre><code class="language-yaml"> worker_listeners:
- type: http
port: 8085
resources:
- names:
- media
</code></pre>
<p>Note that if running multiple media repositories they must be on the same server
and you must configure a single instance to run the background tasks, e.g.:</p>
<pre><code class="language-yaml"> media_instance_running_background_jobs: "media-repository-1"
</code></pre>
<p>Note that if a reverse proxy is used , then <code>/_matrix/media/</code> must be routed for both inbound client and federation requests (if they are handled separately).</p>
<h3 id="synapseappuser_dir"><a class="header" href="#synapseappuser_dir"><code>synapse.app.user_dir</code></a></h3>
<p>Handles searches in the user directory. It can handle REST endpoints matching
the following regular expressions:</p>
<pre><code>^/_matrix/client/(api/v1|r0|unstable)/user_directory/search$
</code></pre>
<p>When using this worker you must also set <code>update_user_directory: False</code> in the
shared configuration file to stop the main synapse running background
jobs related to updating the user directory.</p>
<h3 id="synapseappfrontend_proxy"><a class="header" href="#synapseappfrontend_proxy"><code>synapse.app.frontend_proxy</code></a></h3>
<p>Proxies some frequently-requested client endpoints to add caching and remove
load from the main synapse. It can handle REST endpoints matching the following
regular expressions:</p>
<pre><code>^/_matrix/client/(api/v1|r0|unstable)/keys/upload
</code></pre>
<p>If <code>use_presence</code> is False in the homeserver config, it can also handle REST
endpoints matching the following regular expressions:</p>
<pre><code>^/_matrix/client/(api/v1|r0|unstable)/presence/[^/]+/status
</code></pre>
<p>This "stub" presence handler will pass through <code>GET</code> request but make the
<code>PUT</code> effectively a no-op.</p>
<p>It will proxy any requests it cannot handle to the main synapse instance. It
must therefore be configured with the location of the main instance, via
the <code>worker_main_http_uri</code> setting in the <code>frontend_proxy</code> worker configuration
file. For example:</p>
<pre><code>worker_main_http_uri: http://127.0.0.1:8008
</code></pre>
<h3 id="historical-apps"><a class="header" href="#historical-apps">Historical apps</a></h3>
<p><em>Note:</em> Historically there used to be more apps, however they have been
amalgamated into a single <code>synapse.app.generic_worker</code> app. The remaining apps
are ones that do specific processing unrelated to requests, e.g. the <code>pusher</code>
that handles sending out push notifications for new events. The intention is for
all these to be folded into the <code>generic_worker</code> app and to use config to define
which processes handle the various proccessing such as push notifications.</p>
<h2 id="migration-from-old-config"><a class="header" href="#migration-from-old-config">Migration from old config</a></h2>
<p>There are two main independent changes that have been made: introducing Redis
support and merging apps into <code>synapse.app.generic_worker</code>. Both these changes
are backwards compatible and so no changes to the config are required, however
server admins are encouraged to plan to migrate to Redis as the old style direct
TCP replication config is deprecated.</p>
<p>To migrate to Redis add the <code>redis</code> config as above, and optionally remove the
TCP <code>replication</code> listener from master and <code>worker_replication_port</code> from worker
config.</p>
<p>To migrate apps to use <code>synapse.app.generic_worker</code> simply update the
<code>worker_app</code> option in the worker configs, and where worker are started (e.g.
in systemd service files, but not required for synctl).</p>
<h2 id="architectural-diagram"><a class="header" href="#architectural-diagram">Architectural diagram</a></h2>
<p>The following shows an example setup using Redis and a reverse proxy:</p>
<pre><code> Clients & Federation
|
v
+-----------+
| |
| Reverse |
| Proxy |
| |
+-----------+
| | |
| | | HTTP requests
+-------------------+ | +-----------+
| +---+ |
| | |
v v v
+--------------+ +--------------+ +--------------+ +--------------+
| Main | | Generic | | Generic | | Event |
| Process | | Worker 1 | | Worker 2 | | Persister |
+--------------+ +--------------+ +--------------+ +--------------+
^ ^ | ^ | | ^ | ^ ^
| | | | | | | | | |
| | | | | HTTP | | | | |
| +----------+<--|---|---------+ | | | |
| | +-------------|-->+----------+ |
| | | |
| | | |
v v v v
====================================================================
Redis pub/sub channel
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h3 id="using-synctl-with-workers"><a class="header" href="#using-synctl-with-workers">Using synctl with workers</a></h3>
<p>If you want to use <code>synctl</code> to manage your synapse processes, you will need to
create an an additional configuration file for the main synapse process. That
configuration should look like this:</p>
<pre><code class="language-yaml">worker_app: synapse.app.homeserver
</code></pre>
<p>Additionally, each worker app must be configured with the name of a "pid file",
to which it will write its process ID when it starts. For example, for a
synchrotron, you might write:</p>
<pre><code class="language-yaml">worker_pid_file: /home/matrix/synapse/worker1.pid
</code></pre>
<p>Finally, to actually run your worker-based synapse, you must pass synctl the <code>-a</code>
commandline option to tell it to operate on all the worker configurations found
in the given directory, e.g.:</p>
<pre><code>synctl -a $CONFIG/workers start
</code></pre>
<p>Currently one should always restart all workers when restarting or upgrading
synapse, unless you explicitly know it's safe not to. For instance, restarting
synapse without restarting all the synchrotrons may result in broken typing
notifications.</p>
<p>To manipulate a specific worker, you pass the -w option to synctl:</p>
<pre><code>synctl -w $CONFIG/workers/worker1.yaml restart
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="setting-up-synapse-with-workers-and-systemd"><a class="header" href="#setting-up-synapse-with-workers-and-systemd">Setting up Synapse with Workers and Systemd</a></h1>
<p>This is a setup for managing synapse with systemd, including support for
managing workers. It provides a <code>matrix-synapse</code> service for the master, as
well as a <code>matrix-synapse-worker@</code> service template for any workers you
require. Additionally, to group the required services, it sets up a
<code>matrix-synapse.target</code>.</p>
<p>See the folder <a href="https://github.com/matrix-org/synapse/tree/develop/docs/systemd-with-workers/system/">system</a>
for the systemd unit files.</p>
<p>The folder <a href="https://github.com/matrix-org/synapse/tree/develop/docs/systemd-with-workers/workers/">workers</a>
contains an example configuration for the <code>federation_reader</code> worker.</p>
<h2 id="synapse-configuration-files"><a class="header" href="#synapse-configuration-files">Synapse configuration files</a></h2>
<p>See <a href="systemd-with-workers/../workers.html">the worker documentation</a> for information on how to set up the
configuration files and reverse-proxy correctly.
Below is a sample <code>federation_reader</code> worker configuration file.</p>
<pre><code class="language-yaml">worker_app: synapse.app.federation_reader
worker_name: federation_reader1
worker_replication_host: 127.0.0.1
worker_replication_http_port: 9093
worker_listeners:
- type: http
port: 8011
resources:
- names: [federation]
worker_log_config: /etc/matrix-synapse/federation-reader-log.yaml
</code></pre>
<p>Systemd manages daemonization itself, so ensure that none of the configuration
files set either <code>daemonize</code> or <code>worker_daemonize</code>.</p>
<p>The config files of all workers are expected to be located in
<code>/etc/matrix-synapse/workers</code>. If you want to use a different location, edit
the provided <code>*.service</code> files accordingly.</p>
<p>There is no need for a separate configuration file for the master process.</p>
<h2 id="set-up"><a class="header" href="#set-up">Set up</a></h2>
<ol>
<li>Adjust synapse configuration files as above.</li>
<li>Copy the <code>*.service</code> and <code>*.target</code> files in <a href="https://github.com/matrix-org/synapse/tree/develop/docs/systemd-with-workers/system/">system</a>
to <code>/etc/systemd/system</code>.</li>
<li>Run <code>systemctl daemon-reload</code> to tell systemd to load the new unit files.</li>
<li>Run <code>systemctl enable matrix-synapse.service</code>. This will configure the
synapse master process to be started as part of the <code>matrix-synapse.target</code>
target.</li>
<li>For each worker process to be enabled, run <code>systemctl enable matrix-synapse-worker@<worker_name>.service</code>. For each <code><worker_name></code>, there
should be a corresponding configuration file.
<code>/etc/matrix-synapse/workers/<worker_name>.yaml</code>.</li>
<li>Start all the synapse processes with <code>systemctl start matrix-synapse.target</code>.</li>
<li>Tell systemd to start synapse on boot with <code>systemctl enable matrix-synapse.target</code>.</li>
</ol>
<h2 id="usage"><a class="header" href="#usage">Usage</a></h2>
<p>Once the services are correctly set up, you can use the following commands
to manage your synapse installation:</p>
<pre><code class="language-sh"># Restart Synapse master and all workers
systemctl restart matrix-synapse.target
# Stop Synapse and all workers
systemctl stop matrix-synapse.target
# Restart the master alone
systemctl start matrix-synapse.service
# Restart a specific worker (eg. federation_reader); the master is
# unaffected by this.
systemctl restart matrix-synapse-worker@federation_reader.service
# Add a new worker (assuming all configs are set up already)
systemctl enable matrix-synapse-worker@federation_writer.service
systemctl restart matrix-synapse.target
</code></pre>
<h2 id="hardening"><a class="header" href="#hardening">Hardening</a></h2>
<p><strong>Optional:</strong> If further hardening is desired, the file
<code>override-hardened.conf</code> may be copied from
<a href="https://github.com/matrix-org/synapse/tree/develop/contrib/systemd/">contrib/systemd/override-hardened.conf</a>
in this repository to the location
<code>/etc/systemd/system/matrix-synapse.service.d/override-hardened.conf</code> (the
directory may have to be created). It enables certain sandboxing features in
systemd to further secure the synapse service. You may read the comments to
understand what the override file is doing. The same file will need to be copied to
<code>/etc/systemd/system/matrix-synapse-worker@.service.d/override-hardened-worker.conf</code>
(this directory may also have to be created) in order to apply the same
hardening options to any worker processes.</p>
<p>Once these files have been copied to their appropriate locations, simply reload
systemd's manager config files and restart all Synapse services to apply the hardening options. They will automatically
be applied at every restart as long as the override files are present at the
specified locations.</p>
<pre><code class="language-sh">systemctl daemon-reload
# Restart services
systemctl restart matrix-synapse.target
</code></pre>
<p>In order to see their effect, you may run <code>systemd-analyze security matrix-synapse.service</code> before and after applying the hardening options to see
the changes being applied at a glance.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="administration"><a class="header" href="#administration">Administration</a></h1>
<p>This section contains information on managing your Synapse homeserver. This includes:</p>
<ul>
<li>Managing users, rooms and media via the Admin API.</li>
<li>Setting up metrics and monitoring to give you insight into your homeserver's health.</li>
<li>Configuring structured logging.</li>
</ul>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="the-admin-api"><a class="header" href="#the-admin-api">The Admin API</a></h1>
<h2 id="authenticate-as-a-server-admin"><a class="header" href="#authenticate-as-a-server-admin">Authenticate as a server admin</a></h2>
<p>Many of the API calls in the admin api will require an <code>access_token</code> for a
server admin. (Note that a server admin is distinct from a room admin.)</p>
<p>A user can be marked as a server admin by updating the database directly, e.g.:</p>
<pre><code class="language-sql">UPDATE users SET admin = 1 WHERE name = '@foo:bar.com';
</code></pre>
<p>A new server admin user can also be created using the <code>register_new_matrix_user</code>
command. This is a script that is located in the <code>scripts/</code> directory, or possibly
already on your <code>$PATH</code> depending on how Synapse was installed.</p>
<p>Finding your user's <code>access_token</code> is client-dependent, but will usually be shown in the client's settings.</p>
<h2 id="making-an-admin-api-request"><a class="header" href="#making-an-admin-api-request">Making an Admin API request</a></h2>
<p>Once you have your <code>access_token</code>, you will need to authenticate each request to an Admin API endpoint by
providing the token as either a query parameter or a request header. To add it as a request header in cURL:</p>
<pre><code class="language-sh">curl --header "Authorization: Bearer <access_token>" <the_rest_of_your_API_request>
</code></pre>
<p>For more details on access tokens in Matrix, please refer to the complete
<a href="https://matrix.org/docs/spec/client_server/r0.6.1#using-access-tokens">matrix spec documentation</a>.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="account-validity-api"><a class="header" href="#account-validity-api">Account validity API</a></h1>
<p>This API allows a server administrator to manage the validity of an account. To
use it, you must enable the account validity feature (under
<code>account_validity</code>) in Synapse's configuration.</p>
<h2 id="renew-account"><a class="header" href="#renew-account">Renew account</a></h2>
<p>This API extends the validity of an account by as much time as configured in the
<code>period</code> parameter from the <code>account_validity</code> configuration.</p>
<p>The API is:</p>
<pre><code>POST /_synapse/admin/v1/account_validity/validity
</code></pre>
<p>with the following body:</p>
<pre><code class="language-json">{
"user_id": "<user ID for the account to renew>",
"expiration_ts": 0,
"enable_renewal_emails": true
}
</code></pre>
<p><code>expiration_ts</code> is an optional parameter and overrides the expiration date,
which otherwise defaults to now + validity period.</p>
<p><code>enable_renewal_emails</code> is also an optional parameter and enables/disables
sending renewal emails to the user. Defaults to true.</p>
<p>The API returns with the new expiration date for this account, as a timestamp in
milliseconds since epoch:</p>
<pre><code class="language-json">{
"expiration_ts": 0
}
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="delete-a-local-group"><a class="header" href="#delete-a-local-group">Delete a local group</a></h1>
<p>This API lets a server admin delete a local group. Doing so will kick all
users out of the group so that their clients will correctly handle the group
being deleted.</p>
<p>The API is:</p>
<pre><code>POST /_synapse/admin/v1/delete_group/<group_id>
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: see <a href="admin_api/../usage/administration/admin_api">Admin API</a>.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="show-reported-events"><a class="header" href="#show-reported-events">Show reported events</a></h1>
<p>This API returns information about reported events.</p>
<p>The api is:</p>
<pre><code>GET /_synapse/admin/v1/event_reports?from=0&limit=10
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: see <a href="admin_api/../usage/administration/admin_api">Admin API</a>.</p>
<p>It returns a JSON body like the following:</p>
<pre><code class="language-json">{
"event_reports": [
{
"event_id": "$bNUFCwGzWca1meCGkjp-zwslF-GfVcXukvRLI1_FaVY",
"id": 2,
"reason": "foo",
"score": -100,
"received_ts": 1570897107409,
"canonical_alias": "#alias1:matrix.org",
"room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
"name": "Matrix HQ",
"sender": "@foobar:matrix.org",
"user_id": "@foo:matrix.org"
},
{
"event_id": "$3IcdZsDaN_En-S1DF4EMCy3v4gNRKeOJs8W5qTOKj4I",
"id": 3,
"reason": "bar",
"score": -100,
"received_ts": 1598889612059,
"canonical_alias": "#alias2:matrix.org",
"room_id": "!eGvUQuTCkHGVwNMOjv:matrix.org",
"name": "Your room name here",
"sender": "@foobar:matrix.org",
"user_id": "@bar:matrix.org"
}
],
"next_token": 2,
"total": 4
}
</code></pre>
<p>To paginate, check for <code>next_token</code> and if present, call the endpoint again with <code>from</code>
set to the value of <code>next_token</code>. This will return a new page.</p>
<p>If the endpoint does not return a <code>next_token</code> then there are no more reports to
paginate through.</p>
<p><strong>URL parameters:</strong></p>
<ul>
<li><code>limit</code>: integer - Is optional but is used for pagination, denoting the maximum number
of items to return in this call. Defaults to <code>100</code>.</li>
<li><code>from</code>: integer - Is optional but used for pagination, denoting the offset in the
returned results. This should be treated as an opaque value and not explicitly set to
anything other than the return value of <code>next_token</code> from a previous call. Defaults to <code>0</code>.</li>
<li><code>dir</code>: string - Direction of event report order. Whether to fetch the most recent
first (<code>b</code>) or the oldest first (<code>f</code>). Defaults to <code>b</code>.</li>
<li><code>user_id</code>: string - Is optional and filters to only return users with user IDs that
contain this value. This is the user who reported the event and wrote the reason.</li>
<li><code>room_id</code>: string - Is optional and filters to only return rooms with room IDs that
contain this value.</li>
</ul>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>id</code>: integer - ID of event report.</li>
<li><code>received_ts</code>: integer - The timestamp (in milliseconds since the unix epoch) when this
report was sent.</li>
<li><code>room_id</code>: string - The ID of the room in which the event being reported is located.</li>
<li><code>name</code>: string - The name of the room.</li>
<li><code>event_id</code>: string - The ID of the reported event.</li>
<li><code>user_id</code>: string - This is the user who reported the event and wrote the reason.</li>
<li><code>reason</code>: string - Comment made by the <code>user_id</code> in this report. May be blank or <code>null</code>.</li>
<li><code>score</code>: integer - Content is reported based upon a negative score, where -100 is
"most offensive" and 0 is "inoffensive". May be <code>null</code>.</li>
<li><code>sender</code>: string - This is the ID of the user who sent the original message/event that
was reported.</li>
<li><code>canonical_alias</code>: string - The canonical alias of the room. <code>null</code> if the room does not
have a canonical alias set.</li>
<li><code>next_token</code>: integer - Indication for pagination. See above.</li>
<li><code>total</code>: integer - Total number of event reports related to the query
(<code>user_id</code> and <code>room_id</code>).</li>
</ul>
<h1 id="show-details-of-a-specific-event-report"><a class="header" href="#show-details-of-a-specific-event-report">Show details of a specific event report</a></h1>
<p>This API returns information about a specific event report.</p>
<p>The api is:</p>
<pre><code>GET /_synapse/admin/v1/event_reports/<report_id>
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: see <a href="admin_api/../usage/administration/admin_api">Admin API</a>.</p>
<p>It returns a JSON body like the following:</p>
<pre><code class="language-jsonc">{
"event_id": "$bNUFCwGzWca1meCGkjp-zwslF-GfVcXukvRLI1_FaVY",
"event_json": {
"auth_events": [
"$YK4arsKKcc0LRoe700pS8DSjOvUT4NDv0HfInlMFw2M",
"$oggsNXxzPFRE3y53SUNd7nsj69-QzKv03a1RucHu-ws"
],
"content": {
"body": "matrix.org: This Week in Matrix",
"format": "org.matrix.custom.html",
"formatted_body": "<strong>matrix.org</strong>:<br><a href=\"https://matrix.org/blog/\"><strong>This Week in Matrix</strong></a>",
"msgtype": "m.notice"
},
"depth": 546,
"hashes": {
"sha256": "xK1//xnmvHJIOvbgXlkI8eEqdvoMmihVDJ9J4SNlsAw"
},
"origin": "matrix.org",
"origin_server_ts": 1592291711430,
"prev_events": [
"$YK4arsKKcc0LRoe700pS8DSjOvUT4NDv0HfInlMFw2M"
],
"prev_state": [],
"room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
"sender": "@foobar:matrix.org",
"signatures": {
"matrix.org": {
"ed25519:a_JaEG": "cs+OUKW/iHx5pEidbWxh0UiNNHwe46Ai9LwNz+Ah16aWDNszVIe2gaAcVZfvNsBhakQTew51tlKmL2kspXk/Dg"
}
},
"type": "m.room.message",
"unsigned": {
"age_ts": 1592291711430,
}
},
"id": <report_id>,
"reason": "foo",
"score": -100,
"received_ts": 1570897107409,
"canonical_alias": "#alias1:matrix.org",
"room_id": "!ERAgBpSOcCCuTJqQPk:matrix.org",
"name": "Matrix HQ",
"sender": "@foobar:matrix.org",
"user_id": "@foo:matrix.org"
}
</code></pre>
<p><strong>URL parameters:</strong></p>
<ul>
<li><code>report_id</code>: string - The ID of the event report.</li>
</ul>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>id</code>: integer - ID of event report.</li>
<li><code>received_ts</code>: integer - The timestamp (in milliseconds since the unix epoch) when this
report was sent.</li>
<li><code>room_id</code>: string - The ID of the room in which the event being reported is located.</li>
<li><code>name</code>: string - The name of the room.</li>
<li><code>event_id</code>: string - The ID of the reported event.</li>
<li><code>user_id</code>: string - This is the user who reported the event and wrote the reason.</li>
<li><code>reason</code>: string - Comment made by the <code>user_id</code> in this report. May be blank.</li>
<li><code>score</code>: integer - Content is reported based upon a negative score, where -100 is
"most offensive" and 0 is "inoffensive".</li>
<li><code>sender</code>: string - This is the ID of the user who sent the original message/event that
was reported.</li>
<li><code>canonical_alias</code>: string - The canonical alias of the room. <code>null</code> if the room does not
have a canonical alias set.</li>
<li><code>event_json</code>: object - Details of the original event that was reported.</li>
</ul>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="contents-1"><a class="header" href="#contents-1">Contents</a></h1>
<ul>
<li><a href="admin_api/media_admin_api.html#querying-media">Querying media</a>
<ul>
<li><a href="admin_api/media_admin_api.html#list-all-media-in-a-room">List all media in a room</a></li>
<li><a href="admin_api/media_admin_api.html#list-all-media-uploaded-by-a-user">List all media uploaded by a user</a></li>
</ul>
</li>
<li><a href="admin_api/media_admin_api.html#quarantine-media">Quarantine media</a>
<ul>
<li><a href="admin_api/media_admin_api.html#quarantining-media-by-id">Quarantining media by ID</a></li>
<li><a href="admin_api/media_admin_api.html#remove-media-from-quarantine-by-id">Remove media from quarantine by ID</a></li>
<li><a href="admin_api/media_admin_api.html#quarantining-media-in-a-room">Quarantining media in a room</a></li>
<li><a href="admin_api/media_admin_api.html#quarantining-all-media-of-a-user">Quarantining all media of a user</a></li>
<li><a href="admin_api/media_admin_api.html#protecting-media-from-being-quarantined">Protecting media from being quarantined</a></li>
<li><a href="admin_api/media_admin_api.html#unprotecting-media-from-being-quarantined">Unprotecting media from being quarantined</a></li>
</ul>
</li>
<li><a href="admin_api/media_admin_api.html#delete-local-media">Delete local media</a>
<ul>
<li><a href="admin_api/media_admin_api.html#delete-a-specific-local-media">Delete a specific local media</a></li>
<li><a href="admin_api/media_admin_api.html#delete-local-media-by-date-or-size">Delete local media by date or size</a></li>
</ul>
</li>
<li><a href="admin_api/media_admin_api.html#purge-remote-media-api">Purge Remote Media API</a></li>
</ul>
<h1 id="querying-media"><a class="header" href="#querying-media">Querying media</a></h1>
<p>These APIs allow extracting media information from the homeserver.</p>
<h2 id="list-all-media-in-a-room"><a class="header" href="#list-all-media-in-a-room">List all media in a room</a></h2>
<p>This API gets a list of known media in a room.
However, it only shows media from unencrypted events or rooms.</p>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v1/room/<room_id>/media
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: see <a href="admin_api/../usage/administration/admin_api">Admin API</a>.</p>
<p>The API returns a JSON body like the following:</p>
<pre><code class="language-json">{
"local": [
"mxc://localhost/xwvutsrqponmlkjihgfedcba",
"mxc://localhost/abcdefghijklmnopqrstuvwx"
],
"remote": [
"mxc://matrix.org/xwvutsrqponmlkjihgfedcba",
"mxc://matrix.org/abcdefghijklmnopqrstuvwx"
]
}
</code></pre>
<h2 id="list-all-media-uploaded-by-a-user"><a class="header" href="#list-all-media-uploaded-by-a-user">List all media uploaded by a user</a></h2>
<p>Listing all media that has been uploaded by a local user can be achieved through
the use of the <a href="admin_api/user_admin_api.html#list-media-of-a-user">List media of a user</a>
Admin API.</p>
<h1 id="quarantine-media"><a class="header" href="#quarantine-media">Quarantine media</a></h1>
<p>Quarantining media means that it is marked as inaccessible by users. It applies
to any local media, and any locally-cached copies of remote media.</p>
<p>The media file itself (and any thumbnails) is not deleted from the server.</p>
<h2 id="quarantining-media-by-id"><a class="header" href="#quarantining-media-by-id">Quarantining media by ID</a></h2>
<p>This API quarantines a single piece of local or remote media.</p>
<p>Request:</p>
<pre><code>POST /_synapse/admin/v1/media/quarantine/<server_name>/<media_id>
{}
</code></pre>
<p>Where <code>server_name</code> is in the form of <code>example.org</code>, and <code>media_id</code> is in the
form of <code>abcdefg12345...</code>.</p>
<p>Response:</p>
<pre><code class="language-json">{}
</code></pre>
<h2 id="remove-media-from-quarantine-by-id"><a class="header" href="#remove-media-from-quarantine-by-id">Remove media from quarantine by ID</a></h2>
<p>This API removes a single piece of local or remote media from quarantine.</p>
<p>Request:</p>
<pre><code>POST /_synapse/admin/v1/media/unquarantine/<server_name>/<media_id>
{}
</code></pre>
<p>Where <code>server_name</code> is in the form of <code>example.org</code>, and <code>media_id</code> is in the
form of <code>abcdefg12345...</code>.</p>
<p>Response:</p>
<pre><code class="language-json">{}
</code></pre>
<h2 id="quarantining-media-in-a-room"><a class="header" href="#quarantining-media-in-a-room">Quarantining media in a room</a></h2>
<p>This API quarantines all local and remote media in a room.</p>
<p>Request:</p>
<pre><code>POST /_synapse/admin/v1/room/<room_id>/media/quarantine
{}
</code></pre>
<p>Where <code>room_id</code> is in the form of <code>!roomid12345:example.org</code>.</p>
<p>Response:</p>
<pre><code class="language-json">{
"num_quarantined": 10
}
</code></pre>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>num_quarantined</code>: integer - The number of media items successfully quarantined</li>
</ul>
<p>Note that there is a legacy endpoint, <code>POST /_synapse/admin/v1/quarantine_media/<room_id></code>, that operates the same.
However, it is deprecated and may be removed in a future release.</p>
<h2 id="quarantining-all-media-of-a-user"><a class="header" href="#quarantining-all-media-of-a-user">Quarantining all media of a user</a></h2>
<p>This API quarantines all <em>local</em> media that a <em>local</em> user has uploaded. That is to say, if
you would like to quarantine media uploaded by a user on a remote homeserver, you should
instead use one of the other APIs.</p>
<p>Request:</p>
<pre><code>POST /_synapse/admin/v1/user/<user_id>/media/quarantine
{}
</code></pre>
<p>URL Parameters</p>
<ul>
<li><code>user_id</code>: string - User ID in the form of <code>@bob:example.org</code></li>
</ul>
<p>Response:</p>
<pre><code class="language-json">{
"num_quarantined": 10
}
</code></pre>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>num_quarantined</code>: integer - The number of media items successfully quarantined</li>
</ul>
<h2 id="protecting-media-from-being-quarantined"><a class="header" href="#protecting-media-from-being-quarantined">Protecting media from being quarantined</a></h2>
<p>This API protects a single piece of local media from being quarantined using the
above APIs. This is useful for sticker packs and other shared media which you do
not want to get quarantined, especially when
<a href="admin_api/media_admin_api.html#quarantining-media-in-a-room">quarantining media in a room</a>.</p>
<p>Request:</p>
<pre><code>POST /_synapse/admin/v1/media/protect/<media_id>
{}
</code></pre>
<p>Where <code>media_id</code> is in the form of <code>abcdefg12345...</code>.</p>
<p>Response:</p>
<pre><code class="language-json">{}
</code></pre>
<h2 id="unprotecting-media-from-being-quarantined"><a class="header" href="#unprotecting-media-from-being-quarantined">Unprotecting media from being quarantined</a></h2>
<p>This API reverts the protection of a media.</p>
<p>Request:</p>
<pre><code>POST /_synapse/admin/v1/media/unprotect/<media_id>
{}
</code></pre>
<p>Where <code>media_id</code> is in the form of <code>abcdefg12345...</code>.</p>
<p>Response:</p>
<pre><code class="language-json">{}
</code></pre>
<h1 id="delete-local-media"><a class="header" href="#delete-local-media">Delete local media</a></h1>
<p>This API deletes the <em>local</em> media from the disk of your own server.
This includes any local thumbnails and copies of media downloaded from
remote homeservers.
This API will not affect media that has been uploaded to external
media repositories (e.g https://github.com/turt2live/matrix-media-repo/).
See also <a href="admin_api/media_admin_api.html#purge-remote-media-api">Purge Remote Media API</a>.</p>
<h2 id="delete-a-specific-local-media"><a class="header" href="#delete-a-specific-local-media">Delete a specific local media</a></h2>
<p>Delete a specific <code>media_id</code>.</p>
<p>Request:</p>
<pre><code>DELETE /_synapse/admin/v1/media/<server_name>/<media_id>
{}
</code></pre>
<p>URL Parameters</p>
<ul>
<li><code>server_name</code>: string - The name of your local server (e.g <code>matrix.org</code>)</li>
<li><code>media_id</code>: string - The ID of the media (e.g <code>abcdefghijklmnopqrstuvwx</code>)</li>
</ul>
<p>Response:</p>
<pre><code class="language-json">{
"deleted_media": [
"abcdefghijklmnopqrstuvwx"
],
"total": 1
}
</code></pre>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>deleted_media</code>: an array of strings - List of deleted <code>media_id</code></li>
<li><code>total</code>: integer - Total number of deleted <code>media_id</code></li>
</ul>
<h2 id="delete-local-media-by-date-or-size"><a class="header" href="#delete-local-media-by-date-or-size">Delete local media by date or size</a></h2>
<p>Request:</p>
<pre><code>POST /_synapse/admin/v1/media/<server_name>/delete?before_ts=<before_ts>
{}
</code></pre>
<p>URL Parameters</p>
<ul>
<li><code>server_name</code>: string - The name of your local server (e.g <code>matrix.org</code>).</li>
<li><code>before_ts</code>: string representing a positive integer - Unix timestamp in ms.
Files that were last used before this timestamp will be deleted. It is the timestamp of
last access and not the timestamp creation.</li>
<li><code>size_gt</code>: Optional - string representing a positive integer - Size of the media in bytes.
Files that are larger will be deleted. Defaults to <code>0</code>.</li>
<li><code>keep_profiles</code>: Optional - string representing a boolean - Switch to also delete files
that are still used in image data (e.g user profile, room avatar).
If <code>false</code> these files will be deleted. Defaults to <code>true</code>.</li>
</ul>
<p>Response:</p>
<pre><code class="language-json">{
"deleted_media": [
"abcdefghijklmnopqrstuvwx",
"abcdefghijklmnopqrstuvwz"
],
"total": 2
}
</code></pre>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>deleted_media</code>: an array of strings - List of deleted <code>media_id</code></li>
<li><code>total</code>: integer - Total number of deleted <code>media_id</code></li>
</ul>
<h1 id="purge-remote-media-api"><a class="header" href="#purge-remote-media-api">Purge Remote Media API</a></h1>
<p>The purge remote media API allows server admins to purge old cached remote media.</p>
<p>The API is:</p>
<pre><code>POST /_synapse/admin/v1/purge_media_cache?before_ts=<unix_timestamp_in_ms>
{}
</code></pre>
<p>URL Parameters</p>
<ul>
<li><code>unix_timestamp_in_ms</code>: string representing a positive integer - Unix timestamp in ms.
All cached media that was last accessed before this timestamp will be removed.</li>
</ul>
<p>Response:</p>
<pre><code class="language-json">{
"deleted": 10
}
</code></pre>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>deleted</code>: integer - The number of media items successfully deleted</li>
</ul>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: see <a href="admin_api/../usage/administration/admin_api">Admin API</a>.</p>
<p>If the user re-requests purged remote media, synapse will re-request the media
from the originating server.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="purge-history-api"><a class="header" href="#purge-history-api">Purge History API</a></h1>
<p>The purge history API allows server admins to purge historic events from their
database, reclaiming disk space.</p>
<p>Depending on the amount of history being purged a call to the API may take
several minutes or longer. During this period users will not be able to
paginate further back in the room from the point being purged from.</p>
<p>Note that Synapse requires at least one message in each room, so it will never
delete the last message in a room.</p>
<p>The API is:</p>
<pre><code>POST /_synapse/admin/v1/purge_history/<room_id>[/<event_id>]
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>By default, events sent by local users are not deleted, as they may represent
the only copies of this content in existence. (Events sent by remote users are
deleted.)</p>
<p>Room state data (such as joins, leaves, topic) is always preserved.</p>
<p>To delete local message events as well, set <code>delete_local_events</code> in the body:</p>
<pre><code>{
"delete_local_events": true
}
</code></pre>
<p>The caller must specify the point in the room to purge up to. This can be
specified by including an event_id in the URI, or by setting a
<code>purge_up_to_event_id</code> or <code>purge_up_to_ts</code> in the request body. If an event
id is given, that event (and others at the same graph depth) will be retained.
If <code>purge_up_to_ts</code> is given, it should be a timestamp since the unix epoch,
in milliseconds.</p>
<p>The API starts the purge running, and returns immediately with a JSON body with
a purge id:</p>
<pre><code class="language-json">{
"purge_id": "<opaque id>"
}
</code></pre>
<h2 id="purge-status-query"><a class="header" href="#purge-status-query">Purge status query</a></h2>
<p>It is possible to poll for updates on recent purges with a second API;</p>
<pre><code>GET /_synapse/admin/v1/purge_history_status/<purge_id>
</code></pre>
<p>Again, you will need to authenticate by providing an <code>access_token</code> for a
server admin.</p>
<p>This API returns a JSON body like the following:</p>
<pre><code class="language-json">{
"status": "active"
}
</code></pre>
<p>The status will be one of <code>active</code>, <code>complete</code>, or <code>failed</code>.</p>
<h2 id="reclaim-disk-space-postgres"><a class="header" href="#reclaim-disk-space-postgres">Reclaim disk space (Postgres)</a></h2>
<p>To reclaim the disk space and return it to the operating system, you need to run
<code>VACUUM FULL;</code> on the database.</p>
<p><a href="https://www.postgresql.org/docs/current/sql-vacuum.html">https://www.postgresql.org/docs/current/sql-vacuum.html</a></p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="deprecated-purge-room-api"><a class="header" href="#deprecated-purge-room-api">Deprecated: Purge room API</a></h1>
<p><strong>The old Purge room API is deprecated and will be removed in a future release.
See the new <a href="admin_api/rooms.html#delete-room-api">Delete Room API</a> for more details.</strong></p>
<p>This API will remove all trace of a room from your database.</p>
<p>All local users must have left the room before it can be removed.</p>
<p>The API is:</p>
<pre><code>POST /_synapse/admin/v1/purge_room
{
"room_id": "!room:id"
}
</code></pre>
<p>You must authenticate using the access token of an admin user.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="shared-secret-registration"><a class="header" href="#shared-secret-registration">Shared-Secret Registration</a></h1>
<p>This API allows for the creation of users in an administrative and
non-interactive way. This is generally used for bootstrapping a Synapse
instance with administrator accounts.</p>
<p>To authenticate yourself to the server, you will need both the shared secret
(<code>registration_shared_secret</code> in the homeserver configuration), and a
one-time nonce. If the registration shared secret is not configured, this API
is not enabled.</p>
<p>To fetch the nonce, you need to request one from the API:</p>
<pre><code>> GET /_synapse/admin/v1/register
< {"nonce": "thisisanonce"}
</code></pre>
<p>Once you have the nonce, you can make a <code>POST</code> to the same URL with a JSON
body containing the nonce, username, password, whether they are an admin
(optional, False by default), and a HMAC digest of the content. Also you can
set the displayname (optional, <code>username</code> by default).</p>
<p>As an example:</p>
<pre><code>> POST /_synapse/admin/v1/register
> {
"nonce": "thisisanonce",
"username": "pepper_roni",
"displayname": "Pepper Roni",
"password": "pizza",
"admin": true,
"mac": "mac_digest_here"
}
< {
"access_token": "token_here",
"user_id": "@pepper_roni:localhost",
"home_server": "test",
"device_id": "device_id_here"
}
</code></pre>
<p>The MAC is the hex digest output of the HMAC-SHA1 algorithm, with the key being
the shared secret and the content being the nonce, user, password, either the
string "admin" or "notadmin", and optionally the user_type
each separated by NULs. For an example of generation in Python:</p>
<pre><code class="language-python">import hmac, hashlib
def generate_mac(nonce, user, password, admin=False, user_type=None):
mac = hmac.new(
key=shared_secret,
digestmod=hashlib.sha1,
)
mac.update(nonce.encode('utf8'))
mac.update(b"\x00")
mac.update(user.encode('utf8'))
mac.update(b"\x00")
mac.update(password.encode('utf8'))
mac.update(b"\x00")
mac.update(b"admin" if admin else b"notadmin")
if user_type:
mac.update(b"\x00")
mac.update(user_type.encode('utf8'))
return mac.hexdigest()
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="edit-room-membership-api"><a class="header" href="#edit-room-membership-api">Edit Room Membership API</a></h1>
<p>This API allows an administrator to join an user account with a given <code>user_id</code>
to a room with a given <code>room_id_or_alias</code>. You can only modify the membership of
local users. The server administrator must be in the room and have permission to
invite users.</p>
<h2 id="parameters"><a class="header" href="#parameters">Parameters</a></h2>
<p>The following parameters are available:</p>
<ul>
<li><code>user_id</code> - Fully qualified user: for example, <code>@user:server.com</code>.</li>
<li><code>room_id_or_alias</code> - The room identifier or alias to join: for example,
<code>!636q39766251:server.com</code>.</li>
</ul>
<h2 id="usage-1"><a class="header" href="#usage-1">Usage</a></h2>
<pre><code>POST /_synapse/admin/v1/join/<room_id_or_alias>
{
"user_id": "@user:server.com"
}
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: see <a href="admin_api/../usage/administration/admin_api">Admin API</a>.</p>
<p>Response:</p>
<pre><code>{
"room_id": "!636q39766251:server.com"
}
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="contents-2"><a class="header" href="#contents-2">Contents</a></h1>
<ul>
<li><a href="admin_api/rooms.html#list-room-api">List Room API</a></li>
<li><a href="admin_api/rooms.html#room-details-api">Room Details API</a></li>
<li><a href="admin_api/rooms.html#room-members-api">Room Members API</a></li>
<li><a href="admin_api/rooms.html#room-state-api">Room State API</a></li>
<li><a href="admin_api/rooms.html#delete-room-api">Delete Room API</a>
<ul>
<li><a href="admin_api/rooms.html#undoing-room-shutdowns">Undoing room shutdowns</a></li>
</ul>
</li>
<li><a href="admin_api/rooms.html#make-room-admin-api">Make Room Admin API</a></li>
<li><a href="admin_api/rooms.html#forward-extremities-admin-api">Forward Extremities Admin API</a></li>
<li><a href="admin_api/rooms.html#event-context-api">Event Context API</a></li>
</ul>
<h1 id="list-room-api"><a class="header" href="#list-room-api">List Room API</a></h1>
<p>The List Room admin API allows server admins to get a list of rooms on their
server. There are various parameters available that allow for filtering and
sorting the returned list. This API supports pagination.</p>
<p><strong>Parameters</strong></p>
<p>The following query parameters are available:</p>
<ul>
<li><code>from</code> - Offset in the returned list. Defaults to <code>0</code>.</li>
<li><code>limit</code> - Maximum amount of rooms to return. Defaults to <code>100</code>.</li>
<li><code>order_by</code> - The method in which to sort the returned list of rooms. Valid values are:
<ul>
<li><code>alphabetical</code> - Same as <code>name</code>. This is deprecated.</li>
<li><code>size</code> - Same as <code>joined_members</code>. This is deprecated.</li>
<li><code>name</code> - Rooms are ordered alphabetically by room name. This is the default.</li>
<li><code>canonical_alias</code> - Rooms are ordered alphabetically by main alias address of the room.</li>
<li><code>joined_members</code> - Rooms are ordered by the number of members. Largest to smallest.</li>
<li><code>joined_local_members</code> - Rooms are ordered by the number of local members. Largest to smallest.</li>
<li><code>version</code> - Rooms are ordered by room version. Largest to smallest.</li>
<li><code>creator</code> - Rooms are ordered alphabetically by creator of the room.</li>
<li><code>encryption</code> - Rooms are ordered alphabetically by the end-to-end encryption algorithm.</li>
<li><code>federatable</code> - Rooms are ordered by whether the room is federatable.</li>
<li><code>public</code> - Rooms are ordered by visibility in room list.</li>
<li><code>join_rules</code> - Rooms are ordered alphabetically by join rules of the room.</li>
<li><code>guest_access</code> - Rooms are ordered alphabetically by guest access option of the room.</li>
<li><code>history_visibility</code> - Rooms are ordered alphabetically by visibility of history of the room.</li>
<li><code>state_events</code> - Rooms are ordered by number of state events. Largest to smallest.</li>
</ul>
</li>
<li><code>dir</code> - Direction of room order. Either <code>f</code> for forwards or <code>b</code> for backwards. Setting
this value to <code>b</code> will reverse the above sort order. Defaults to <code>f</code>.</li>
<li><code>search_term</code> - Filter rooms by their room name. Search term can be contained in any
part of the room name. Defaults to no filtering.</li>
</ul>
<p><strong>Response</strong></p>
<p>The following fields are possible in the JSON response body:</p>
<ul>
<li><code>rooms</code> - An array of objects, each containing information about a room.
<ul>
<li>Room objects contain the following fields:
<ul>
<li><code>room_id</code> - The ID of the room.</li>
<li><code>name</code> - The name of the room.</li>
<li><code>canonical_alias</code> - The canonical (main) alias address of the room.</li>
<li><code>joined_members</code> - How many users are currently in the room.</li>
<li><code>joined_local_members</code> - How many local users are currently in the room.</li>
<li><code>version</code> - The version of the room as a string.</li>
<li><code>creator</code> - The <code>user_id</code> of the room creator.</li>
<li><code>encryption</code> - Algorithm of end-to-end encryption of messages. Is <code>null</code> if encryption is not active.</li>
<li><code>federatable</code> - Whether users on other servers can join this room.</li>
<li><code>public</code> - Whether the room is visible in room directory.</li>
<li><code>join_rules</code> - The type of rules used for users wishing to join this room. One of: ["public", "knock", "invite", "private"].</li>
<li><code>guest_access</code> - Whether guests can join the room. One of: ["can_join", "forbidden"].</li>
<li><code>history_visibility</code> - Who can see the room history. One of: ["invited", "joined", "shared", "world_readable"].</li>
<li><code>state_events</code> - Total number of state_events of a room. Complexity of the room.</li>
</ul>
</li>
</ul>
</li>
<li><code>offset</code> - The current pagination offset in rooms. This parameter should be
used instead of <code>next_token</code> for room offset as <code>next_token</code> is
not intended to be parsed.</li>
<li><code>total_rooms</code> - The total number of rooms this query can return. Using this
and <code>offset</code>, you have enough information to know the current
progression through the list.</li>
<li><code>next_batch</code> - If this field is present, we know that there are potentially
more rooms on the server that did not all fit into this response.
We can use <code>next_batch</code> to get the "next page" of results. To do
so, simply repeat your request, setting the <code>from</code> parameter to
the value of <code>next_batch</code>.</li>
<li><code>prev_batch</code> - If this field is present, it is possible to paginate backwards.
Use <code>prev_batch</code> for the <code>from</code> value in the next request to
get the "previous page" of results.</li>
</ul>
<p>The API is:</p>
<p>A standard request with no filtering:</p>
<pre><code>GET /_synapse/admin/v1/rooms
</code></pre>
<p>A response body like the following is returned:</p>
<pre><code class="language-jsonc">{
"rooms": [
{
"room_id": "!OGEhHVWSdvArJzumhm:matrix.org",
"name": "Matrix HQ",
"canonical_alias": "#matrix:matrix.org",
"joined_members": 8326,
"joined_local_members": 2,
"version": "1",
"creator": "@foo:matrix.org",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
},
... (8 hidden items) ...
{
"room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
"name": "This Week In Matrix (TWIM)",
"canonical_alias": "#twim:matrix.org",
"joined_members": 314,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:matrix.org",
"encryption": "m.megolm.v1.aes-sha2",
"federatable": true,
"public": false,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8345
}
],
"offset": 0,
"total_rooms": 10
}
</code></pre>
<p>Filtering by room name:</p>
<pre><code>GET /_synapse/admin/v1/rooms?search_term=TWIM
</code></pre>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"rooms": [
{
"room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
"name": "This Week In Matrix (TWIM)",
"canonical_alias": "#twim:matrix.org",
"joined_members": 314,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:matrix.org",
"encryption": "m.megolm.v1.aes-sha2",
"federatable": true,
"public": false,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8
}
],
"offset": 0,
"total_rooms": 1
}
</code></pre>
<p>Paginating through a list of rooms:</p>
<pre><code>GET /_synapse/admin/v1/rooms?order_by=size
</code></pre>
<p>A response body like the following is returned:</p>
<pre><code class="language-jsonc">{
"rooms": [
{
"room_id": "!OGEhHVWSdvArJzumhm:matrix.org",
"name": "Matrix HQ",
"canonical_alias": "#matrix:matrix.org",
"joined_members": 8326,
"joined_local_members": 2,
"version": "1",
"creator": "@foo:matrix.org",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
},
... (98 hidden items) ...
{
"room_id": "!xYvNcQPhnkrdUmYczI:matrix.org",
"name": "This Week In Matrix (TWIM)",
"canonical_alias": "#twim:matrix.org",
"joined_members": 314,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:matrix.org",
"encryption": "m.megolm.v1.aes-sha2",
"federatable": true,
"public": false,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8345
}
],
"offset": 0,
"total_rooms": 150
"next_token": 100
}
</code></pre>
<p>The presence of the <code>next_token</code> parameter tells us that there are more rooms
than returned in this request, and we need to make another request to get them.
To get the next batch of room results, we repeat our request, setting the <code>from</code>
parameter to the value of <code>next_token</code>.</p>
<pre><code>GET /_synapse/admin/v1/rooms?order_by=size&from=100
</code></pre>
<p>A response body like the following is returned:</p>
<pre><code class="language-jsonc">{
"rooms": [
{
"room_id": "!mscvqgqpHYjBGDxNym:matrix.org",
"name": "Music Theory",
"canonical_alias": "#musictheory:matrix.org",
"joined_members": 127,
"joined_local_members": 2,
"version": "1",
"creator": "@foo:matrix.org",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
},
... (48 hidden items) ...
{
"room_id": "!twcBhHVdZlQWuuxBhN:termina.org.uk",
"name": "weechat-matrix",
"canonical_alias": "#weechat-matrix:termina.org.uk",
"joined_members": 137,
"joined_local_members": 20,
"version": "4",
"creator": "@foo:termina.org.uk",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 8345
}
],
"offset": 100,
"prev_batch": 0,
"total_rooms": 150
}
</code></pre>
<p>Once the <code>next_token</code> parameter is no longer present, we know we've reached the
end of the list.</p>
<h1 id="room-details-api"><a class="header" href="#room-details-api">Room Details API</a></h1>
<p>The Room Details admin API allows server admins to get all details of a room.</p>
<p>The following fields are possible in the JSON response body:</p>
<ul>
<li><code>room_id</code> - The ID of the room.</li>
<li><code>name</code> - The name of the room.</li>
<li><code>topic</code> - The topic of the room.</li>
<li><code>avatar</code> - The <code>mxc</code> URI to the avatar of the room.</li>
<li><code>canonical_alias</code> - The canonical (main) alias address of the room.</li>
<li><code>joined_members</code> - How many users are currently in the room.</li>
<li><code>joined_local_members</code> - How many local users are currently in the room.</li>
<li><code>joined_local_devices</code> - How many local devices are currently in the room.</li>
<li><code>version</code> - The version of the room as a string.</li>
<li><code>creator</code> - The <code>user_id</code> of the room creator.</li>
<li><code>encryption</code> - Algorithm of end-to-end encryption of messages. Is <code>null</code> if encryption is not active.</li>
<li><code>federatable</code> - Whether users on other servers can join this room.</li>
<li><code>public</code> - Whether the room is visible in room directory.</li>
<li><code>join_rules</code> - The type of rules used for users wishing to join this room. One of: ["public", "knock", "invite", "private"].</li>
<li><code>guest_access</code> - Whether guests can join the room. One of: ["can_join", "forbidden"].</li>
<li><code>history_visibility</code> - Who can see the room history. One of: ["invited", "joined", "shared", "world_readable"].</li>
<li><code>state_events</code> - Total number of state_events of a room. Complexity of the room.</li>
</ul>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v1/rooms/<room_id>
</code></pre>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"room_id": "!mscvqgqpHYjBGDxNym:matrix.org",
"name": "Music Theory",
"avatar": "mxc://matrix.org/AQDaVFlbkQoErdOgqWRgiGSV",
"topic": "Theory, Composition, Notation, Analysis",
"canonical_alias": "#musictheory:matrix.org",
"joined_members": 127,
"joined_local_members": 2,
"joined_local_devices": 2,
"version": "1",
"creator": "@foo:matrix.org",
"encryption": null,
"federatable": true,
"public": true,
"join_rules": "invite",
"guest_access": null,
"history_visibility": "shared",
"state_events": 93534
}
</code></pre>
<h1 id="room-members-api"><a class="header" href="#room-members-api">Room Members API</a></h1>
<p>The Room Members admin API allows server admins to get a list of all members of a room.</p>
<p>The response includes the following fields:</p>
<ul>
<li><code>members</code> - A list of all the members that are present in the room, represented by their ids.</li>
<li><code>total</code> - Total number of members in the room.</li>
</ul>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v1/rooms/<room_id>/members
</code></pre>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"members": [
"@foo:matrix.org",
"@bar:matrix.org",
"@foobar:matrix.org"
],
"total": 3
}
</code></pre>
<h1 id="room-state-api"><a class="header" href="#room-state-api">Room State API</a></h1>
<p>The Room State admin API allows server admins to get a list of all state events in a room.</p>
<p>The response includes the following fields:</p>
<ul>
<li><code>state</code> - The current state of the room at the time of request.</li>
</ul>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v1/rooms/<room_id>/state
</code></pre>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"state": [
{"type": "m.room.create", "state_key": "", "etc": true},
{"type": "m.room.power_levels", "state_key": "", "etc": true},
{"type": "m.room.name", "state_key": "", "etc": true}
]
}
</code></pre>
<h1 id="delete-room-api"><a class="header" href="#delete-room-api">Delete Room API</a></h1>
<p>The Delete Room admin API allows server admins to remove rooms from server
and block these rooms.</p>
<p>Shuts down a room. Moves all local users and room aliases automatically to a
new room if <code>new_room_user_id</code> is set. Otherwise local users only
leave the room without any information.</p>
<p>The new room will be created with the user specified by the <code>new_room_user_id</code> parameter
as room administrator and will contain a message explaining what happened. Users invited
to the new room will have power level <code>-10</code> by default, and thus be unable to speak.</p>
<p>If <code>block</code> is <code>True</code> it prevents new joins to the old room.</p>
<p>This API will remove all trace of the old room from your database after removing
all local users. If <code>purge</code> is <code>true</code> (the default), all traces of the old room will
be removed from your database after removing all local users. If you do not want
this to happen, set <code>purge</code> to <code>false</code>.
Depending on the amount of history being purged a call to the API may take
several minutes or longer.</p>
<p>The local server will only have the power to move local user and room aliases to
the new room. Users on other servers will be unaffected.</p>
<p>The API is:</p>
<pre><code>DELETE /_synapse/admin/v1/rooms/<room_id>
</code></pre>
<p>with a body of:</p>
<pre><code class="language-json">{
"new_room_user_id": "@someuser:example.com",
"room_name": "Content Violation Notification",
"message": "Bad Room has been shutdown due to content violations on this server. Please review our Terms of Service.",
"block": true,
"purge": true
}
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: see <a href="admin_api/../usage/administration/admin_api">Admin API</a>.</p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"kicked_users": [
"@foobar:example.com"
],
"failed_to_kick_users": [],
"local_aliases": [
"#badroom:example.com",
"#evilsaloon:example.com"
],
"new_room_id": "!newroomid:example.com"
}
</code></pre>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>room_id</code> - The ID of the room.</li>
</ul>
<p>The following JSON body parameters are available:</p>
<ul>
<li><code>new_room_user_id</code> - Optional. If set, a new room will be created with this user ID
as the creator and admin, and all users in the old room will be moved into that
room. If not set, no new room will be created and the users will just be removed
from the old room. The user ID must be on the local server, but does not necessarily
have to belong to a registered user.</li>
<li><code>room_name</code> - Optional. A string representing the name of the room that new users will be
invited to. Defaults to <code>Content Violation Notification</code></li>
<li><code>message</code> - Optional. A string containing the first message that will be sent as
<code>new_room_user_id</code> in the new room. Ideally this will clearly convey why the
original room was shut down. Defaults to <code>Sharing illegal content on this server is not permitted and rooms in violation will be blocked.</code></li>
<li><code>block</code> - Optional. If set to <code>true</code>, this room will be added to a blocking list, preventing
future attempts to join the room. Defaults to <code>false</code>.</li>
<li><code>purge</code> - Optional. If set to <code>true</code>, it will remove all traces of the room from your database.
Defaults to <code>true</code>.</li>
<li><code>force_purge</code> - Optional, and ignored unless <code>purge</code> is <code>true</code>. If set to <code>true</code>, it
will force a purge to go ahead even if there are local users still in the room. Do not
use this unless a regular <code>purge</code> operation fails, as it could leave those users'
clients in a confused state.</li>
</ul>
<p>The JSON body must not be empty. The body must be at least <code>{}</code>.</p>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>kicked_users</code> - An array of users (<code>user_id</code>) that were kicked.</li>
<li><code>failed_to_kick_users</code> - An array of users (<code>user_id</code>) that that were not kicked.</li>
<li><code>local_aliases</code> - An array of strings representing the local aliases that were migrated from
the old room to the new.</li>
<li><code>new_room_id</code> - A string representing the room ID of the new room.</li>
</ul>
<h2 id="undoing-room-shutdowns"><a class="header" href="#undoing-room-shutdowns">Undoing room shutdowns</a></h2>
<p><em>Note</em>: This guide may be outdated by the time you read it. By nature of room shutdowns being performed at the database level,
the structure can and does change without notice.</p>
<p>First, it's important to understand that a room shutdown is very destructive. Undoing a shutdown is not as simple as pretending it
never happened - work has to be done to move forward instead of resetting the past. In fact, in some cases it might not be possible
to recover at all:</p>
<ul>
<li>If the room was invite-only, your users will need to be re-invited.</li>
<li>If the room no longer has any members at all, it'll be impossible to rejoin.</li>
<li>The first user to rejoin will have to do so via an alias on a different server.</li>
</ul>
<p>With all that being said, if you still want to try and recover the room:</p>
<ol>
<li>For safety reasons, shut down Synapse.</li>
<li>In the database, run <code>DELETE FROM blocked_rooms WHERE room_id = '!example:example.org';</code>
<ul>
<li>For caution: it's recommended to run this in a transaction: <code>BEGIN; DELETE ...;</code>, verify you got 1 result, then <code>COMMIT;</code>.</li>
<li>The room ID is the same one supplied to the shutdown room API, not the Content Violation room.</li>
</ul>
</li>
<li>Restart Synapse.</li>
</ol>
<p>You will have to manually handle, if you so choose, the following:</p>
<ul>
<li>Aliases that would have been redirected to the Content Violation room.</li>
<li>Users that would have been booted from the room (and will have been force-joined to the Content Violation room).</li>
<li>Removal of the Content Violation room if desired.</li>
</ul>
<h2 id="deprecated-endpoint"><a class="header" href="#deprecated-endpoint">Deprecated endpoint</a></h2>
<p>The previous deprecated API will be removed in a future release, it was:</p>
<pre><code>POST /_synapse/admin/v1/rooms/<room_id>/delete
</code></pre>
<p>It behaves the same way than the current endpoint except the path and the method.</p>
<h1 id="make-room-admin-api"><a class="header" href="#make-room-admin-api">Make Room Admin API</a></h1>
<p>Grants another user the highest power available to a local user who is in the room.
If the user is not in the room, and it is not publicly joinable, then invite the user.</p>
<p>By default the server admin (the caller) is granted power, but another user can
optionally be specified, e.g.:</p>
<pre><code>POST /_synapse/admin/v1/rooms/<room_id_or_alias>/make_room_admin
{
"user_id": "@foo:example.com"
}
</code></pre>
<h1 id="forward-extremities-admin-api"><a class="header" href="#forward-extremities-admin-api">Forward Extremities Admin API</a></h1>
<p>Enables querying and deleting forward extremities from rooms. When a lot of forward
extremities accumulate in a room, performance can become degraded. For details, see
<a href="https://github.com/matrix-org/synapse/issues/1760">#1760</a>.</p>
<h2 id="check-for-forward-extremities"><a class="header" href="#check-for-forward-extremities">Check for forward extremities</a></h2>
<p>To check the status of forward extremities for a room:</p>
<pre><code>GET /_synapse/admin/v1/rooms/<room_id_or_alias>/forward_extremities
</code></pre>
<p>A response as follows will be returned:</p>
<pre><code class="language-json">{
"count": 1,
"results": [
{
"event_id": "$M5SP266vsnxctfwFgFLNceaCo3ujhRtg_NiiHabcdefgh",
"state_group": 439,
"depth": 123,
"received_ts": 1611263016761
}
]
}
</code></pre>
<h2 id="deleting-forward-extremities"><a class="header" href="#deleting-forward-extremities">Deleting forward extremities</a></h2>
<p><strong>WARNING</strong>: Please ensure you know what you're doing and have read
the related issue <a href="https://github.com/matrix-org/synapse/issues/1760">#1760</a>.
Under no situations should this API be executed as an automated maintenance task!</p>
<p>If a room has lots of forward extremities, the extra can be
deleted as follows:</p>
<pre><code>DELETE /_synapse/admin/v1/rooms/<room_id_or_alias>/forward_extremities
</code></pre>
<p>A response as follows will be returned, indicating the amount of forward extremities
that were deleted.</p>
<pre><code class="language-json">{
"deleted": 1
}
</code></pre>
<h1 id="event-context-api"><a class="header" href="#event-context-api">Event Context API</a></h1>
<p>This API lets a client find the context of an event. This is designed primarily to investigate abuse reports.</p>
<pre><code>GET /_synapse/admin/v1/rooms/<room_id>/context/<event_id>
</code></pre>
<p>This API mimmicks <a href="https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-context-eventid">GET /_matrix/client/r0/rooms/{roomId}/context/{eventId}</a>. Please refer to the link for all details on parameters and reseponse.</p>
<p>Example response:</p>
<pre><code class="language-json">{
"end": "t29-57_2_0_2",
"events_after": [
{
"content": {
"body": "This is an example text message",
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"formatted_body": "<b>This is an example text message</b>"
},
"type": "m.room.message",
"event_id": "$143273582443PhrSn:example.org",
"room_id": "!636q39766251:example.com",
"sender": "@example:example.org",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
}
}
],
"event": {
"content": {
"body": "filename.jpg",
"info": {
"h": 398,
"w": 394,
"mimetype": "image/jpeg",
"size": 31037
},
"url": "mxc://example.org/JWEIFJgwEIhweiWJE",
"msgtype": "m.image"
},
"type": "m.room.message",
"event_id": "$f3h4d129462ha:example.com",
"room_id": "!636q39766251:example.com",
"sender": "@example:example.org",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
}
},
"events_before": [
{
"content": {
"body": "something-important.doc",
"filename": "something-important.doc",
"info": {
"mimetype": "application/msword",
"size": 46144
},
"msgtype": "m.file",
"url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe"
},
"type": "m.room.message",
"event_id": "$143273582443PhrSn:example.org",
"room_id": "!636q39766251:example.com",
"sender": "@example:example.org",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
}
}
],
"start": "t27-54_2_0_2",
"state": [
{
"content": {
"creator": "@example:example.org",
"room_version": "1",
"m.federate": true,
"predecessor": {
"event_id": "$something:example.org",
"room_id": "!oldroom:example.org"
}
},
"type": "m.room.create",
"event_id": "$143273582443PhrSn:example.org",
"room_id": "!636q39766251:example.com",
"sender": "@example:example.org",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
},
"state_key": ""
},
{
"content": {
"membership": "join",
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Alice Margatroid"
},
"type": "m.room.member",
"event_id": "$143273582443PhrSn:example.org",
"room_id": "!636q39766251:example.com",
"sender": "@example:example.org",
"origin_server_ts": 1432735824653,
"unsigned": {
"age": 1234
},
"state_key": "@alice:example.org"
}
]
}
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="server-notices-1"><a class="header" href="#server-notices-1">Server Notices</a></h1>
<p>The API to send notices is as follows:</p>
<pre><code>POST /_synapse/admin/v1/send_server_notice
</code></pre>
<p>or:</p>
<pre><code>PUT /_synapse/admin/v1/send_server_notice/{txnId}
</code></pre>
<p>You will need to authenticate with an access token for an admin user.</p>
<p>When using the <code>PUT</code> form, retransmissions with the same transaction ID will be
ignored in the same way as with <code>PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}</code>.</p>
<p>The request body should look something like the following:</p>
<pre><code class="language-json">{
"user_id": "@target_user:server_name",
"content": {
"msgtype": "m.text",
"body": "This is my message"
}
}
</code></pre>
<p>You can optionally include the following additional parameters:</p>
<ul>
<li><code>type</code>: the type of event. Defaults to <code>m.room.message</code>.</li>
<li><code>state_key</code>: Setting this will result in a state event being sent.</li>
</ul>
<p>Once the notice has been sent, the API will return the following response:</p>
<pre><code class="language-json">{
"event_id": "<event_id>"
}
</code></pre>
<p>Note that server notices must be enabled in <code>homeserver.yaml</code> before this API
can be used. See <a href="admin_api/../server_notices.html">the server notices documentation</a> for more information.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="deprecated-shutdown-room-api"><a class="header" href="#deprecated-shutdown-room-api">Deprecated: Shutdown room API</a></h1>
<p><strong>The old Shutdown room API is deprecated and will be removed in a future release.
See the new <a href="admin_api/rooms.html#delete-room-api">Delete Room API</a> for more details.</strong></p>
<p>Shuts down a room, preventing new joins and moves local users and room aliases automatically
to a new room. The new room will be created with the user specified by the
<code>new_room_user_id</code> parameter as room administrator and will contain a message
explaining what happened. Users invited to the new room will have power level
-10 by default, and thus be unable to speak. The old room's power levels will be changed to
disallow any further invites or joins.</p>
<p>The local server will only have the power to move local user and room aliases to
the new room. Users on other servers will be unaffected.</p>
<h2 id="api-1"><a class="header" href="#api-1">API</a></h2>
<p>You will need to authenticate with an access token for an admin user.</p>
<h3 id="url"><a class="header" href="#url">URL</a></h3>
<p><code>POST /_synapse/admin/v1/shutdown_room/{room_id}</code></p>
<h3 id="url-parameters"><a class="header" href="#url-parameters">URL Parameters</a></h3>
<ul>
<li><code>room_id</code> - The ID of the room (e.g <code>!someroom:example.com</code>)</li>
</ul>
<h3 id="json-body-parameters"><a class="header" href="#json-body-parameters">JSON Body Parameters</a></h3>
<ul>
<li><code>new_room_user_id</code> - Required. A string representing the user ID of the user that will admin
the new room that all users in the old room will be moved to.</li>
<li><code>room_name</code> - Optional. A string representing the name of the room that new users will be
invited to.</li>
<li><code>message</code> - Optional. A string containing the first message that will be sent as
<code>new_room_user_id</code> in the new room. Ideally this will clearly convey why the
original room was shut down.</li>
</ul>
<p>If not specified, the default value of <code>room_name</code> is "Content Violation
Notification". The default value of <code>message</code> is "Sharing illegal content on
othis server is not permitted and rooms in violation will be blocked."</p>
<h3 id="response-parameters"><a class="header" href="#response-parameters">Response Parameters</a></h3>
<ul>
<li><code>kicked_users</code> - An integer number representing the number of users that
were kicked.</li>
<li><code>failed_to_kick_users</code> - An integer number representing the number of users
that were not kicked.</li>
<li><code>local_aliases</code> - An array of strings representing the local aliases that were migrated from
the old room to the new.</li>
<li><code>new_room_id</code> - A string representing the room ID of the new room.</li>
</ul>
<h2 id="example-4"><a class="header" href="#example-4">Example</a></h2>
<p>Request:</p>
<pre><code>POST /_synapse/admin/v1/shutdown_room/!somebadroom%3Aexample.com
{
"new_room_user_id": "@someuser:example.com",
"room_name": "Content Violation Notification",
"message": "Bad Room has been shutdown due to content violations on this server. Please review our Terms of Service."
}
</code></pre>
<p>Response:</p>
<pre><code>{
"kicked_users": 5,
"failed_to_kick_users": 0,
"local_aliases": ["#badroom:example.com", "#evilsaloon:example.com],
"new_room_id": "!newroomid:example.com",
},
</code></pre>
<h2 id="undoing-room-shutdowns-1"><a class="header" href="#undoing-room-shutdowns-1">Undoing room shutdowns</a></h2>
<p><em>Note</em>: This guide may be outdated by the time you read it. By nature of room shutdowns being performed at the database level,
the structure can and does change without notice.</p>
<p>First, it's important to understand that a room shutdown is very destructive. Undoing a shutdown is not as simple as pretending it
never happened - work has to be done to move forward instead of resetting the past. In fact, in some cases it might not be possible
to recover at all:</p>
<ul>
<li>If the room was invite-only, your users will need to be re-invited.</li>
<li>If the room no longer has any members at all, it'll be impossible to rejoin.</li>
<li>The first user to rejoin will have to do so via an alias on a different server.</li>
</ul>
<p>With all that being said, if you still want to try and recover the room:</p>
<ol>
<li>For safety reasons, shut down Synapse.</li>
<li>In the database, run <code>DELETE FROM blocked_rooms WHERE room_id = '!example:example.org';</code>
<ul>
<li>For caution: it's recommended to run this in a transaction: <code>BEGIN; DELETE ...;</code>, verify you got 1 result, then <code>COMMIT;</code>.</li>
<li>The room ID is the same one supplied to the shutdown room API, not the Content Violation room.</li>
</ul>
</li>
<li>Restart Synapse.</li>
</ol>
<p>You will have to manually handle, if you so choose, the following:</p>
<ul>
<li>Aliases that would have been redirected to the Content Violation room.</li>
<li>Users that would have been booted from the room (and will have been force-joined to the Content Violation room).</li>
<li>Removal of the Content Violation room if desired.</li>
</ul>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="users-media-usage-statistics"><a class="header" href="#users-media-usage-statistics">Users' media usage statistics</a></h1>
<p>Returns information about all local media usage of users. Gives the
possibility to filter them by time and user.</p>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v1/statistics/users/media
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code>
for a server admin: see <a href="admin_api/../usage/administration/admin_api">Admin API</a>.</p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"users": [
{
"displayname": "foo_user_0",
"media_count": 2,
"media_length": 134,
"user_id": "@foo_user_0:test"
},
{
"displayname": "foo_user_1",
"media_count": 2,
"media_length": 134,
"user_id": "@foo_user_1:test"
}
],
"next_token": 3,
"total": 10
}
</code></pre>
<p>To paginate, check for <code>next_token</code> and if present, call the endpoint
again with <code>from</code> set to the value of <code>next_token</code>. This will return a new page.</p>
<p>If the endpoint does not return a <code>next_token</code> then there are no more
reports to paginate through.</p>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>limit</code>: string representing a positive integer - Is optional but is
used for pagination, denoting the maximum number of items to return
in this call. Defaults to <code>100</code>.</li>
<li><code>from</code>: string representing a positive integer - Is optional but used for pagination,
denoting the offset in the returned results. This should be treated as an opaque value
and not explicitly set to anything other than the return value of <code>next_token</code> from a
previous call. Defaults to <code>0</code>.</li>
<li><code>order_by</code> - string - The method in which to sort the returned list of users. Valid values are:
<ul>
<li><code>user_id</code> - Users are ordered alphabetically by <code>user_id</code>. This is the default.</li>
<li><code>displayname</code> - Users are ordered alphabetically by <code>displayname</code>.</li>
<li><code>media_length</code> - Users are ordered by the total size of uploaded media in bytes.
Smallest to largest.</li>
<li><code>media_count</code> - Users are ordered by number of uploaded media. Smallest to largest.</li>
</ul>
</li>
<li><code>from_ts</code> - string representing a positive integer - Considers only
files created at this timestamp or later. Unix timestamp in ms.</li>
<li><code>until_ts</code> - string representing a positive integer - Considers only
files created at this timestamp or earlier. Unix timestamp in ms.</li>
<li><code>search_term</code> - string - Filter users by their user ID localpart <strong>or</strong> displayname.
The search term can be found in any part of the string.
Defaults to no filtering.</li>
<li><code>dir</code> - string - Direction of order. Either <code>f</code> for forwards or <code>b</code> for backwards.
Setting this value to <code>b</code> will reverse the above sort order. Defaults to <code>f</code>.</li>
</ul>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>users</code> - An array of objects, each containing information
about the user and their local media. Objects contain the following fields:
<ul>
<li><code>displayname</code> - string - Displayname of this user.</li>
<li><code>media_count</code> - integer - Number of uploaded media by this user.</li>
<li><code>media_length</code> - integer - Size of uploaded media in bytes by this user.</li>
<li><code>user_id</code> - string - Fully-qualified user ID (ex. <code>@user:server.com</code>).</li>
</ul>
</li>
<li><code>next_token</code> - integer - Opaque value used for pagination. See above.</li>
<li><code>total</code> - integer - Total number of users after filtering.</li>
</ul>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="user-admin-api"><a class="header" href="#user-admin-api">User Admin API</a></h1>
<h2 id="query-user-account"><a class="header" href="#query-user-account">Query User Account</a></h2>
<p>This API returns information about a specific user account.</p>
<p>The api is:</p>
<pre><code>GET /_synapse/admin/v2/users/<user_id>
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>It returns a JSON body like the following:</p>
<pre><code class="language-json">{
"displayname": "User",
"threepids": [
{
"medium": "email",
"address": "<user_mail_1>"
},
{
"medium": "email",
"address": "<user_mail_2>"
}
],
"avatar_url": "<avatar_url>",
"admin": 0,
"deactivated": 0,
"shadow_banned": 0,
"password_hash": "$2b$12$p9B4GkqYdRTPGD",
"creation_ts": 1560432506,
"appservice_id": null,
"consent_server_notice_sent": null,
"consent_version": null,
"external_ids": [
{
"auth_provider": "<provider1>",
"external_id": "<user_id_provider_1>"
},
{
"auth_provider": "<provider2>",
"external_id": "<user_id_provider_2>"
}
]
}
</code></pre>
<p>URL parameters:</p>
<ul>
<li><code>user_id</code>: fully-qualified user id: for example, <code>@user:server.com</code>.</li>
</ul>
<h2 id="create-or-modify-account"><a class="header" href="#create-or-modify-account">Create or modify Account</a></h2>
<p>This API allows an administrator to create or modify a user account with a
specific <code>user_id</code>.</p>
<p>This api is:</p>
<pre><code>PUT /_synapse/admin/v2/users/<user_id>
</code></pre>
<p>with a body of:</p>
<pre><code class="language-json">{
"password": "user_password",
"displayname": "User",
"threepids": [
{
"medium": "email",
"address": "<user_mail_1>"
},
{
"medium": "email",
"address": "<user_mail_2>"
}
],
"avatar_url": "<avatar_url>",
"admin": false,
"deactivated": false
}
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>URL parameters:</p>
<ul>
<li><code>user_id</code>: fully-qualified user id: for example, <code>@user:server.com</code>.</li>
</ul>
<p>Body parameters:</p>
<ul>
<li>
<p><code>password</code>, optional. If provided, the user's password is updated and all
devices are logged out.</p>
</li>
<li>
<p><code>displayname</code>, optional, defaults to the value of <code>user_id</code>.</p>
</li>
<li>
<p><code>threepids</code>, optional, allows setting the third-party IDs (email, msisdn)
belonging to a user.</p>
</li>
<li>
<p><code>avatar_url</code>, optional, must be a
<a href="https://matrix.org/docs/spec/client_server/r0.6.0#matrix-content-mxc-uris">MXC URI</a>.</p>
</li>
<li>
<p><code>admin</code>, optional, defaults to <code>false</code>.</p>
</li>
<li>
<p><code>deactivated</code>, optional. If unspecified, deactivation state will be left
unchanged on existing accounts and set to <code>false</code> for new accounts.
A user cannot be erased by deactivating with this API. For details on
deactivating users see <a href="admin_api/user_admin_api.html#deactivate-account">Deactivate Account</a>.</p>
</li>
</ul>
<p>If the user already exists then optional parameters default to the current value.</p>
<p>In order to re-activate an account <code>deactivated</code> must be set to <code>false</code>. If
users do not login via single-sign-on, a new <code>password</code> must be provided.</p>
<h2 id="list-accounts"><a class="header" href="#list-accounts">List Accounts</a></h2>
<p>This API returns all local user accounts.
By default, the response is ordered by ascending user ID.</p>
<pre><code>GET /_synapse/admin/v2/users?from=0&limit=10&guests=false
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"users": [
{
"name": "<user_id1>",
"is_guest": 0,
"admin": 0,
"user_type": null,
"deactivated": 0,
"shadow_banned": 0,
"displayname": "<User One>",
"avatar_url": null,
"creation_ts": 1560432668000
}, {
"name": "<user_id2>",
"is_guest": 0,
"admin": 1,
"user_type": null,
"deactivated": 0,
"shadow_banned": 0,
"displayname": "<User Two>",
"avatar_url": "<avatar_url>",
"creation_ts": 1561550621000
}
],
"next_token": "100",
"total": 200
}
</code></pre>
<p>To paginate, check for <code>next_token</code> and if present, call the endpoint again
with <code>from</code> set to the value of <code>next_token</code>. This will return a new page.</p>
<p>If the endpoint does not return a <code>next_token</code> then there are no more users
to paginate through.</p>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li>
<p><code>user_id</code> - Is optional and filters to only return users with user IDs
that contain this value. This parameter is ignored when using the <code>name</code> parameter.</p>
</li>
<li>
<p><code>name</code> - Is optional and filters to only return users with user ID localparts
<strong>or</strong> displaynames that contain this value.</p>
</li>
<li>
<p><code>guests</code> - string representing a bool - Is optional and if <code>false</code> will <strong>exclude</strong> guest users.
Defaults to <code>true</code> to include guest users.</p>
</li>
<li>
<p><code>deactivated</code> - string representing a bool - Is optional and if <code>true</code> will <strong>include</strong> deactivated users.
Defaults to <code>false</code> to exclude deactivated users.</p>
</li>
<li>
<p><code>limit</code> - string representing a positive integer - Is optional but is used for pagination,
denoting the maximum number of items to return in this call. Defaults to <code>100</code>.</p>
</li>
<li>
<p><code>from</code> - string representing a positive integer - Is optional but used for pagination,
denoting the offset in the returned results. This should be treated as an opaque value and
not explicitly set to anything other than the return value of <code>next_token</code> from a previous call.
Defaults to <code>0</code>.</p>
</li>
<li>
<p><code>order_by</code> - The method by which to sort the returned list of users.
If the ordered field has duplicates, the second order is always by ascending <code>name</code>,
which guarantees a stable ordering. Valid values are:</p>
<ul>
<li><code>name</code> - Users are ordered alphabetically by <code>name</code>. This is the default.</li>
<li><code>is_guest</code> - Users are ordered by <code>is_guest</code> status.</li>
<li><code>admin</code> - Users are ordered by <code>admin</code> status.</li>
<li><code>user_type</code> - Users are ordered alphabetically by <code>user_type</code>.</li>
<li><code>deactivated</code> - Users are ordered by <code>deactivated</code> status.</li>
<li><code>shadow_banned</code> - Users are ordered by <code>shadow_banned</code> status.</li>
<li><code>displayname</code> - Users are ordered alphabetically by <code>displayname</code>.</li>
<li><code>avatar_url</code> - Users are ordered alphabetically by avatar URL.</li>
<li><code>creation_ts</code> - Users are ordered by when the users was created in ms.</li>
</ul>
</li>
<li>
<p><code>dir</code> - Direction of media order. Either <code>f</code> for forwards or <code>b</code> for backwards.
Setting this value to <code>b</code> will reverse the above sort order. Defaults to <code>f</code>.</p>
</li>
</ul>
<p>Caution. The database only has indexes on the columns <code>name</code> and <code>creation_ts</code>.
This means that if a different sort order is used (<code>is_guest</code>, <code>admin</code>,
<code>user_type</code>, <code>deactivated</code>, <code>shadow_banned</code>, <code>avatar_url</code> or <code>displayname</code>),
this can cause a large load on the database, especially for large environments.</p>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li>
<p><code>users</code> - An array of objects, each containing information about an user.
User objects contain the following fields:</p>
<ul>
<li><code>name</code> - string - Fully-qualified user ID (ex. <code>@user:server.com</code>).</li>
<li><code>is_guest</code> - bool - Status if that user is a guest account.</li>
<li><code>admin</code> - bool - Status if that user is a server administrator.</li>
<li><code>user_type</code> - string - Type of the user. Normal users are type <code>None</code>.
This allows user type specific behaviour. There are also types <code>support</code> and <code>bot</code>. </li>
<li><code>deactivated</code> - bool - Status if that user has been marked as deactivated.</li>
<li><code>shadow_banned</code> - bool - Status if that user has been marked as shadow banned.</li>
<li><code>displayname</code> - string - The user's display name if they have set one.</li>
<li><code>avatar_url</code> - string - The user's avatar URL if they have set one.</li>
<li><code>creation_ts</code> - integer - The user's creation timestamp in ms.</li>
</ul>
</li>
<li>
<p><code>next_token</code>: string representing a positive integer - Indication for pagination. See above.</p>
</li>
<li>
<p><code>total</code> - integer - Total number of media.</p>
</li>
</ul>
<h2 id="query-current-sessions-for-a-user"><a class="header" href="#query-current-sessions-for-a-user">Query current sessions for a user</a></h2>
<p>This API returns information about the active sessions for a specific user.</p>
<p>The endpoints are:</p>
<pre><code>GET /_synapse/admin/v1/whois/<user_id>
</code></pre>
<p>and:</p>
<pre><code>GET /_matrix/client/r0/admin/whois/<userId>
</code></pre>
<p>See also: <a href="https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-admin-whois-userid">Client Server
API Whois</a>.</p>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>It returns a JSON body like the following:</p>
<pre><code class="language-json">{
"user_id": "<user_id>",
"devices": {
"": {
"sessions": [
{
"connections": [
{
"ip": "1.2.3.4",
"last_seen": 1417222374433,
"user_agent": "Mozilla/5.0 ..."
},
{
"ip": "1.2.3.10",
"last_seen": 1417222374500,
"user_agent": "Dalvik/2.1.0 ..."
}
]
}
]
}
}
}
</code></pre>
<p><code>last_seen</code> is measured in milliseconds since the Unix epoch.</p>
<h2 id="deactivate-account"><a class="header" href="#deactivate-account">Deactivate Account</a></h2>
<p>This API deactivates an account. It removes active access tokens, resets the
password, and deletes third-party IDs (to prevent the user requesting a
password reset).</p>
<p>It can also mark the user as GDPR-erased. This means messages sent by the
user will still be visible by anyone that was in the room when these messages
were sent, but hidden from users joining the room afterwards.</p>
<p>The api is:</p>
<pre><code>POST /_synapse/admin/v1/deactivate/<user_id>
</code></pre>
<p>with a body of:</p>
<pre><code class="language-json">{
"erase": true
}
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>The erase parameter is optional and defaults to <code>false</code>.
An empty body may be passed for backwards compatibility.</p>
<p>The following actions are performed when deactivating an user:</p>
<ul>
<li>Try to unpind 3PIDs from the identity server</li>
<li>Remove all 3PIDs from the homeserver</li>
<li>Delete all devices and E2EE keys</li>
<li>Delete all access tokens</li>
<li>Delete the password hash</li>
<li>Removal from all rooms the user is a member of</li>
<li>Remove the user from the user directory</li>
<li>Reject all pending invites</li>
<li>Remove all account validity information related to the user</li>
</ul>
<p>The following additional actions are performed during deactivation if <code>erase</code>
is set to <code>true</code>:</p>
<ul>
<li>Remove the user's display name</li>
<li>Remove the user's avatar URL</li>
<li>Mark the user as erased</li>
</ul>
<h2 id="reset-password"><a class="header" href="#reset-password">Reset password</a></h2>
<p>Changes the password of another user. This will automatically log the user out of all their devices.</p>
<p>The api is:</p>
<pre><code>POST /_synapse/admin/v1/reset_password/<user_id>
</code></pre>
<p>with a body of:</p>
<pre><code class="language-json">{
"new_password": "<secret>",
"logout_devices": true
}
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>The parameter <code>new_password</code> is required.
The parameter <code>logout_devices</code> is optional and defaults to <code>true</code>.</p>
<h2 id="get-whether-a-user-is-a-server-administrator-or-not"><a class="header" href="#get-whether-a-user-is-a-server-administrator-or-not">Get whether a user is a server administrator or not</a></h2>
<p>The api is:</p>
<pre><code>GET /_synapse/admin/v1/users/<user_id>/admin
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"admin": true
}
</code></pre>
<h2 id="change-whether-a-user-is-a-server-administrator-or-not"><a class="header" href="#change-whether-a-user-is-a-server-administrator-or-not">Change whether a user is a server administrator or not</a></h2>
<p>Note that you cannot demote yourself.</p>
<p>The api is:</p>
<pre><code>PUT /_synapse/admin/v1/users/<user_id>/admin
</code></pre>
<p>with a body of:</p>
<pre><code class="language-json">{
"admin": true
}
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<h2 id="list-room-memberships-of-a-user"><a class="header" href="#list-room-memberships-of-a-user">List room memberships of a user</a></h2>
<p>Gets a list of all <code>room_id</code> that a specific <code>user_id</code> is member.</p>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v1/users/<user_id>/joined_rooms
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json"> {
"joined_rooms": [
"!DuGcnbhHGaSZQoNQR:matrix.org",
"!ZtSaPCawyWtxfWiIy:matrix.org"
],
"total": 2
}
</code></pre>
<p>The server returns the list of rooms of which the user and the server
are member. If the user is local, all the rooms of which the user is
member are returned.</p>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - fully qualified: for example, <code>@user:server.com</code>.</li>
</ul>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>joined_rooms</code> - An array of <code>room_id</code>.</li>
<li><code>total</code> - Number of rooms.</li>
</ul>
<h2 id="list-media-of-a-user"><a class="header" href="#list-media-of-a-user">List media of a user</a></h2>
<p>Gets a list of all local media that a specific <code>user_id</code> has created.
By default, the response is ordered by descending creation date and ascending media ID.
The newest media is on top. You can change the order with parameters
<code>order_by</code> and <code>dir</code>.</p>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v1/users/<user_id>/media
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"media": [
{
"created_ts": 100400,
"last_access_ts": null,
"media_id": "qXhyRzulkwLsNHTbpHreuEgo",
"media_length": 67,
"media_type": "image/png",
"quarantined_by": null,
"safe_from_quarantine": false,
"upload_name": "test1.png"
},
{
"created_ts": 200400,
"last_access_ts": null,
"media_id": "FHfiSnzoINDatrXHQIXBtahw",
"media_length": 67,
"media_type": "image/png",
"quarantined_by": null,
"safe_from_quarantine": false,
"upload_name": "test2.png"
}
],
"next_token": 3,
"total": 2
}
</code></pre>
<p>To paginate, check for <code>next_token</code> and if present, call the endpoint again
with <code>from</code> set to the value of <code>next_token</code>. This will return a new page.</p>
<p>If the endpoint does not return a <code>next_token</code> then there are no more
reports to paginate through.</p>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li>
<p><code>user_id</code> - string - fully qualified: for example, <code>@user:server.com</code>.</p>
</li>
<li>
<p><code>limit</code>: string representing a positive integer - Is optional but is used for pagination,
denoting the maximum number of items to return in this call. Defaults to <code>100</code>.</p>
</li>
<li>
<p><code>from</code>: string representing a positive integer - Is optional but used for pagination,
denoting the offset in the returned results. This should be treated as an opaque value and
not explicitly set to anything other than the return value of <code>next_token</code> from a previous call.
Defaults to <code>0</code>.</p>
</li>
<li>
<p><code>order_by</code> - The method by which to sort the returned list of media.
If the ordered field has duplicates, the second order is always by ascending <code>media_id</code>,
which guarantees a stable ordering. Valid values are:</p>
<ul>
<li><code>media_id</code> - Media are ordered alphabetically by <code>media_id</code>.</li>
<li><code>upload_name</code> - Media are ordered alphabetically by name the media was uploaded with.</li>
<li><code>created_ts</code> - Media are ordered by when the content was uploaded in ms.
Smallest to largest. This is the default.</li>
<li><code>last_access_ts</code> - Media are ordered by when the content was last accessed in ms.
Smallest to largest.</li>
<li><code>media_length</code> - Media are ordered by length of the media in bytes.
Smallest to largest.</li>
<li><code>media_type</code> - Media are ordered alphabetically by MIME-type.</li>
<li><code>quarantined_by</code> - Media are ordered alphabetically by the user ID that
initiated the quarantine request for this media.</li>
<li><code>safe_from_quarantine</code> - Media are ordered by the status if this media is safe
from quarantining.</li>
</ul>
</li>
<li>
<p><code>dir</code> - Direction of media order. Either <code>f</code> for forwards or <code>b</code> for backwards.
Setting this value to <code>b</code> will reverse the above sort order. Defaults to <code>f</code>.</p>
</li>
</ul>
<p>If neither <code>order_by</code> nor <code>dir</code> is set, the default order is newest media on top
(corresponds to <code>order_by</code> = <code>created_ts</code> and <code>dir</code> = <code>b</code>).</p>
<p>Caution. The database only has indexes on the columns <code>media_id</code>,
<code>user_id</code> and <code>created_ts</code>. This means that if a different sort order is used
(<code>upload_name</code>, <code>last_access_ts</code>, <code>media_length</code>, <code>media_type</code>,
<code>quarantined_by</code> or <code>safe_from_quarantine</code>), this can cause a large load on the
database, especially for large environments.</p>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li>
<p><code>media</code> - An array of objects, each containing information about a media.
Media objects contain the following fields:</p>
<ul>
<li>
<p><code>created_ts</code> - integer - Timestamp when the content was uploaded in ms.</p>
</li>
<li>
<p><code>last_access_ts</code> - integer - Timestamp when the content was last accessed in ms.</p>
</li>
<li>
<p><code>media_id</code> - string - The id used to refer to the media.</p>
</li>
<li>
<p><code>media_length</code> - integer - Length of the media in bytes.</p>
</li>
<li>
<p><code>media_type</code> - string - The MIME-type of the media.</p>
</li>
<li>
<p><code>quarantined_by</code> - string - The user ID that initiated the quarantine request
for this media.</p>
</li>
<li>
<p><code>safe_from_quarantine</code> - bool - Status if this media is safe from quarantining.</p>
</li>
<li>
<p><code>upload_name</code> - string - The name the media was uploaded with.</p>
</li>
</ul>
</li>
<li>
<p><code>next_token</code>: integer - Indication for pagination. See above.</p>
</li>
<li>
<p><code>total</code> - integer - Total number of media.</p>
</li>
</ul>
<h2 id="login-as-a-user"><a class="header" href="#login-as-a-user">Login as a user</a></h2>
<p>Get an access token that can be used to authenticate as that user. Useful for
when admins wish to do actions on behalf of a user.</p>
<p>The API is:</p>
<pre><code>POST /_synapse/admin/v1/users/<user_id>/login
{}
</code></pre>
<p>An optional <code>valid_until_ms</code> field can be specified in the request body as an
integer timestamp that specifies when the token should expire. By default tokens
do not expire.</p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"access_token": "<opaque_access_token_string>"
}
</code></pre>
<p>This API does <em>not</em> generate a new device for the user, and so will not appear
their <code>/devices</code> list, and in general the target user should not be able to
tell they have been logged in as.</p>
<p>To expire the token call the standard <code>/logout</code> API with the token.</p>
<p>Note: The token will expire if the <em>admin</em> user calls <code>/logout/all</code> from any
of their devices, but the token will <em>not</em> expire if the target user does the
same.</p>
<h2 id="user-devices"><a class="header" href="#user-devices">User devices</a></h2>
<h3 id="list-all-devices"><a class="header" href="#list-all-devices">List all devices</a></h3>
<p>Gets information about all devices for a specific <code>user_id</code>.</p>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v2/users/<user_id>/devices
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"devices": [
{
"device_id": "QBUAZIFURK",
"display_name": "android",
"last_seen_ip": "1.2.3.4",
"last_seen_ts": 1474491775024,
"user_id": "<user_id>"
},
{
"device_id": "AUIECTSRND",
"display_name": "ios",
"last_seen_ip": "1.2.3.5",
"last_seen_ts": 1474491775025,
"user_id": "<user_id>"
}
],
"total": 2
}
</code></pre>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - fully qualified: for example, <code>@user:server.com</code>.</li>
</ul>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li>
<p><code>devices</code> - An array of objects, each containing information about a device.
Device objects contain the following fields:</p>
<ul>
<li><code>device_id</code> - Identifier of device.</li>
<li><code>display_name</code> - Display name set by the user for this device.
Absent if no name has been set.</li>
<li><code>last_seen_ip</code> - The IP address where this device was last seen.
(May be a few minutes out of date, for efficiency reasons).</li>
<li><code>last_seen_ts</code> - 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).</li>
<li><code>user_id</code> - Owner of device.</li>
</ul>
</li>
<li>
<p><code>total</code> - Total number of user's devices.</p>
</li>
</ul>
<h3 id="delete-multiple-devices"><a class="header" href="#delete-multiple-devices">Delete multiple devices</a></h3>
<p>Deletes the given devices for a specific <code>user_id</code>, and invalidates
any access token associated with them.</p>
<p>The API is:</p>
<pre><code>POST /_synapse/admin/v2/users/<user_id>/delete_devices
{
"devices": [
"QBUAZIFURK",
"AUIECTSRND"
],
}
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>An empty JSON dict is returned.</p>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - fully qualified: for example, <code>@user:server.com</code>.</li>
</ul>
<p>The following fields are required in the JSON request body:</p>
<ul>
<li><code>devices</code> - The list of device IDs to delete.</li>
</ul>
<h3 id="show-a-device"><a class="header" href="#show-a-device">Show a device</a></h3>
<p>Gets information on a single device, by <code>device_id</code> for a specific <code>user_id</code>.</p>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v2/users/<user_id>/devices/<device_id>
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"device_id": "<device_id>",
"display_name": "android",
"last_seen_ip": "1.2.3.4",
"last_seen_ts": 1474491775024,
"user_id": "<user_id>"
}
</code></pre>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - fully qualified: for example, <code>@user:server.com</code>.</li>
<li><code>device_id</code> - The device to retrieve.</li>
</ul>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>device_id</code> - Identifier of device.</li>
<li><code>display_name</code> - Display name set by the user for this device.
Absent if no name has been set.</li>
<li><code>last_seen_ip</code> - The IP address where this device was last seen.
(May be a few minutes out of date, for efficiency reasons).</li>
<li><code>last_seen_ts</code> - 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).</li>
<li><code>user_id</code> - Owner of device.</li>
</ul>
<h3 id="update-a-device"><a class="header" href="#update-a-device">Update a device</a></h3>
<p>Updates the metadata on the given <code>device_id</code> for a specific <code>user_id</code>.</p>
<p>The API is:</p>
<pre><code>PUT /_synapse/admin/v2/users/<user_id>/devices/<device_id>
{
"display_name": "My other phone"
}
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>An empty JSON dict is returned.</p>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - fully qualified: for example, <code>@user:server.com</code>.</li>
<li><code>device_id</code> - The device to update.</li>
</ul>
<p>The following fields are required in the JSON request body:</p>
<ul>
<li><code>display_name</code> - The new display name for this device. If not given,
the display name is unchanged.</li>
</ul>
<h3 id="delete-a-device"><a class="header" href="#delete-a-device">Delete a device</a></h3>
<p>Deletes the given <code>device_id</code> for a specific <code>user_id</code>,
and invalidates any access token associated with it.</p>
<p>The API is:</p>
<pre><code>DELETE /_synapse/admin/v2/users/<user_id>/devices/<device_id>
{}
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>An empty JSON dict is returned.</p>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - fully qualified: for example, <code>@user:server.com</code>.</li>
<li><code>device_id</code> - The device to delete.</li>
</ul>
<h2 id="list-all-pushers"><a class="header" href="#list-all-pushers">List all pushers</a></h2>
<p>Gets information about all pushers for a specific <code>user_id</code>.</p>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v1/users/<user_id>/pushers
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"pushers": [
{
"app_display_name":"HTTP Push Notifications",
"app_id":"m.http",
"data": {
"url":"example.com"
},
"device_display_name":"pushy push",
"kind":"http",
"lang":"None",
"profile_tag":"",
"pushkey":"a@example.com"
}
],
"total": 1
}
</code></pre>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - fully qualified: for example, <code>@user:server.com</code>.</li>
</ul>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li>
<p><code>pushers</code> - An array containing the current pushers for the user</p>
<ul>
<li>
<p><code>app_display_name</code> - string - A string that will allow the user to identify
what application owns this pusher.</p>
</li>
<li>
<p><code>app_id</code> - string - This is a reverse-DNS style identifier for the application.
Max length, 64 chars.</p>
</li>
<li>
<p><code>data</code> - A dictionary of information for the pusher implementation itself.</p>
<ul>
<li>
<p><code>url</code> - string - Required if <code>kind</code> is <code>http</code>. The URL to use to send
notifications to.</p>
</li>
<li>
<p><code>format</code> - string - The format to use when sending notifications to the
Push Gateway.</p>
</li>
</ul>
</li>
<li>
<p><code>device_display_name</code> - string - A string that will allow the user to identify
what device owns this pusher.</p>
</li>
<li>
<p><code>profile_tag</code> - string - This string determines which set of device specific rules
this pusher executes.</p>
</li>
<li>
<p><code>kind</code> - string - The kind of pusher. "http" is a pusher that sends HTTP pokes.</p>
</li>
<li>
<p><code>lang</code> - string - The preferred language for receiving notifications
(e.g. 'en' or 'en-US')</p>
</li>
<li>
<p><code>profile_tag</code> - string - This string determines which set of device specific rules
this pusher executes.</p>
</li>
<li>
<p><code>pushkey</code> - string - This is a unique identifier for this pusher.
Max length, 512 bytes.</p>
</li>
</ul>
</li>
<li>
<p><code>total</code> - integer - Number of pushers.</p>
</li>
</ul>
<p>See also the
<a href="https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-pushers">Client-Server API Spec on pushers</a>.</p>
<h2 id="shadow-banning-users"><a class="header" href="#shadow-banning-users">Shadow-banning users</a></h2>
<p>Shadow-banning is a useful tool for moderating malicious or egregiously abusive users.
A shadow-banned users receives successful responses to their client-server API requests,
but the events are not propagated into rooms. This can be an effective tool as it
(hopefully) takes longer for the user to realise they are being moderated before
pivoting to another account.</p>
<p>Shadow-banning a user should be used as a tool of last resort and may lead to confusing
or broken behaviour for the client. A shadow-banned user will not receive any
notification and it is generally more appropriate to ban or kick abusive users.
A shadow-banned user will be unable to contact anyone on the server.</p>
<p>The API is:</p>
<pre><code>POST /_synapse/admin/v1/users/<user_id>/shadow_ban
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>An empty JSON dict is returned.</p>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - The fully qualified MXID: for example, <code>@user:server.com</code>. The user must
be local.</li>
</ul>
<h2 id="override-ratelimiting-for-users"><a class="header" href="#override-ratelimiting-for-users">Override ratelimiting for users</a></h2>
<p>This API allows to override or disable ratelimiting for a specific user.
There are specific APIs to set, get and delete a ratelimit.</p>
<h3 id="get-status-of-ratelimit"><a class="header" href="#get-status-of-ratelimit">Get status of ratelimit</a></h3>
<p>The API is:</p>
<pre><code>GET /_synapse/admin/v1/users/<user_id>/override_ratelimit
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"messages_per_second": 0,
"burst_count": 0
}
</code></pre>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - The fully qualified MXID: for example, <code>@user:server.com</code>. The user must
be local.</li>
</ul>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>messages_per_second</code> - integer - The number of actions that can
be performed in a second. <code>0</code> mean that ratelimiting is disabled for this user.</li>
<li><code>burst_count</code> - integer - How many actions that can be performed before
being limited.</li>
</ul>
<p>If <strong>no</strong> custom ratelimit is set, an empty JSON dict is returned.</p>
<pre><code class="language-json">{}
</code></pre>
<h3 id="set-ratelimit"><a class="header" href="#set-ratelimit">Set ratelimit</a></h3>
<p>The API is:</p>
<pre><code>POST /_synapse/admin/v1/users/<user_id>/override_ratelimit
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>A response body like the following is returned:</p>
<pre><code class="language-json">{
"messages_per_second": 0,
"burst_count": 0
}
</code></pre>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - The fully qualified MXID: for example, <code>@user:server.com</code>. The user must
be local.</li>
</ul>
<p>Body parameters:</p>
<ul>
<li><code>messages_per_second</code> - positive integer, optional. The number of actions that can
be performed in a second. Defaults to <code>0</code>.</li>
<li><code>burst_count</code> - positive integer, optional. How many actions that can be performed
before being limited. Defaults to <code>0</code>.</li>
</ul>
<p>To disable users' ratelimit set both values to <code>0</code>.</p>
<p><strong>Response</strong></p>
<p>The following fields are returned in the JSON response body:</p>
<ul>
<li><code>messages_per_second</code> - integer - The number of actions that can
be performed in a second.</li>
<li><code>burst_count</code> - integer - How many actions that can be performed before
being limited.</li>
</ul>
<h3 id="delete-ratelimit"><a class="header" href="#delete-ratelimit">Delete ratelimit</a></h3>
<p>The API is:</p>
<pre><code>DELETE /_synapse/admin/v1/users/<user_id>/override_ratelimit
</code></pre>
<p>To use it, you will need to authenticate by providing an <code>access_token</code> for a
server admin: <a href="admin_api/../usage/administration/admin_api">Admin API</a></p>
<p>An empty JSON dict is returned.</p>
<pre><code class="language-json">{}
</code></pre>
<p><strong>Parameters</strong></p>
<p>The following parameters should be set in the URL:</p>
<ul>
<li><code>user_id</code> - The fully qualified MXID: for example, <code>@user:server.com</code>. The user must
be local.</li>
</ul>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="version-api"><a class="header" href="#version-api">Version API</a></h1>
<p>This API returns the running Synapse version and the Python version
on which Synapse is being run. This is useful when a Synapse instance
is behind a proxy that does not forward the 'Server' header (which also
contains Synapse version information).</p>
<p>The api is:</p>
<pre><code>GET /_synapse/admin/v1/server_version
</code></pre>
<p>It returns a JSON body like the following:</p>
<pre><code class="language-json">{
"server_version": "0.99.2rc1 (b=develop, abcdef123)",
"python_version": "3.6.8"
}
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="using-the-synapse-manhole"><a class="header" href="#using-the-synapse-manhole">Using the synapse manhole</a></h1>
<p>The "manhole" allows server administrators to access a Python shell on a running
Synapse installation. This is a very powerful mechanism for administration and
debugging.</p>
<p><strong><em>Security Warning</em></strong></p>
<p>Note that this will give administrative access to synapse to <strong>all users</strong> with
shell access to the server. It should therefore <strong>not</strong> be enabled in
environments where untrusted users have shell access.</p>
<hr />
<p>To enable it, first uncomment the <code>manhole</code> listener configuration in
<code>homeserver.yaml</code>. The configuration is slightly different if you're using docker.</p>
<h4 id="docker-config"><a class="header" href="#docker-config">Docker config</a></h4>
<p>If you are using Docker, set <code>bind_addresses</code> to <code>['0.0.0.0']</code> as shown:</p>
<pre><code class="language-yaml">listeners:
- port: 9000
bind_addresses: ['0.0.0.0']
type: manhole
</code></pre>
<p>When using <code>docker run</code> to start the server, you will then need to change the command to the following to include the
<code>manhole</code> port forwarding. The <code>-p 127.0.0.1:9000:9000</code> below is important: it
ensures that access to the <code>manhole</code> is only possible for local users.</p>
<pre><code class="language-bash">docker run -d --name synapse \
--mount type=volume,src=synapse-data,dst=/data \
-p 8008:8008 \
-p 127.0.0.1:9000:9000 \
matrixdotorg/synapse:latest
</code></pre>
<h4 id="native-config"><a class="header" href="#native-config">Native config</a></h4>
<p>If you are not using docker, set <code>bind_addresses</code> to <code>['::1', '127.0.0.1']</code> as shown.
The <code>bind_addresses</code> in the example below is important: it ensures that access to the
<code>manhole</code> is only possible for local users).</p>
<pre><code class="language-yaml">listeners:
- port: 9000
bind_addresses: ['::1', '127.0.0.1']
type: manhole
</code></pre>
<h4 id="accessing-synapse-manhole"><a class="header" href="#accessing-synapse-manhole">Accessing synapse manhole</a></h4>
<p>Then restart synapse, and point an ssh client at port 9000 on localhost, using
the username <code>matrix</code>:</p>
<pre><code class="language-bash">ssh -p9000 matrix@localhost
</code></pre>
<p>The password is <code>rabbithole</code>.</p>
<p>This gives a Python REPL in which <code>hs</code> gives access to the
<code>synapse.server.HomeServer</code> object - which in turn gives access to many other
parts of the process.</p>
<p>Note that any call which returns a coroutine will need to be wrapped in <code>ensureDeferred</code>.</p>
<p>As a simple example, retrieving an event from the database:</p>
<pre><code class="language-pycon">>>> from twisted.internet import defer
>>> defer.ensureDeferred(hs.get_datastore().get_event('$1416420717069yeQaw:matrix.org'))
<Deferred at 0x7ff253fc6998 current result: <FrozenEvent event_id='$1416420717069yeQaw:matrix.org', type='m.room.create', state_key=''>>
</code></pre>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="how-to-monitor-synapse-metrics-using-prometheus"><a class="header" href="#how-to-monitor-synapse-metrics-using-prometheus">How to monitor Synapse metrics using Prometheus</a></h1>
<ol>
<li>
<p>Install Prometheus:</p>
<p>Follow instructions at
<a href="http://prometheus.io/docs/introduction/install/">http://prometheus.io/docs/introduction/install/</a></p>
</li>
<li>
<p>Enable Synapse metrics:</p>
<p>There are two methods of enabling metrics in Synapse.</p>
<p>The first serves the metrics as a part of the usual web server and
can be enabled by adding the "metrics" resource to the existing
listener as such:</p>
<pre><code class="language-yaml"> resources:
- names:
- client
- metrics
</code></pre>
<p>This provides a simple way of adding metrics to your Synapse
installation, and serves under <code>/_synapse/metrics</code>. If you do not
wish your metrics be publicly exposed, you will need to either
filter it out at your load balancer, or use the second method.</p>
<p>The second method runs the metrics server on a different port, in a
different thread to Synapse. This can make it more resilient to
heavy load meaning metrics cannot be retrieved, and can be exposed
to just internal networks easier. The served metrics are available
over HTTP only, and will be available at <code>/_synapse/metrics</code>.</p>
<p>Add a new listener to homeserver.yaml:</p>
<pre><code class="language-yaml"> listeners:
- type: metrics
port: 9000
bind_addresses:
- '0.0.0.0'
</code></pre>
<p>For both options, you will need to ensure that <code>enable_metrics</code> is
set to <code>True</code>.</p>
</li>
<li>
<p>Restart Synapse.</p>
</li>
<li>
<p>Add a Prometheus target for Synapse.</p>
<p>It needs to set the <code>metrics_path</code> to a non-default value (under
<code>scrape_configs</code>):</p>
<pre><code class="language-yaml"> - job_name: "synapse"
scrape_interval: 15s
metrics_path: "/_synapse/metrics"
static_configs:
- targets: ["my.server.here:port"]
</code></pre>
<p>where <code>my.server.here</code> is the IP address of Synapse, and <code>port</code> is
the listener port configured with the <code>metrics</code> resource.</p>
<p>If your prometheus is older than 1.5.2, you will need to replace
<code>static_configs</code> in the above with <code>target_groups</code>.</p>
</li>
<li>
<p>Restart Prometheus.</p>
</li>
<li>
<p>Consider using the <a href="https://github.com/matrix-org/synapse/tree/master/contrib/grafana/">grafana dashboard</a>
and required <a href="https://github.com/matrix-org/synapse/tree/master/contrib/prometheus/">recording rules</a> </p>
</li>
</ol>
<h2 id="monitoring-workers"><a class="header" href="#monitoring-workers">Monitoring workers</a></h2>
<p>To monitor a Synapse installation using <a href="workers.html">workers</a>,
every worker needs to be monitored independently, in addition to
the main homeserver process. This is because workers don't send
their metrics to the main homeserver process, but expose them
directly (if they are configured to do so).</p>
<p>To allow collecting metrics from a worker, you need to add a
<code>metrics</code> listener to its configuration, by adding the following
under <code>worker_listeners</code>:</p>
<pre><code class="language-yaml"> - type: metrics
bind_address: ''
port: 9101
</code></pre>
<p>The <code>bind_address</code> and <code>port</code> parameters should be set so that
the resulting listener can be reached by prometheus, and they
don't clash with an existing worker.
With this example, the worker's metrics would then be available
on <code>http://127.0.0.1:9101</code>.</p>
<p>Example Prometheus target for Synapse with workers:</p>
<pre><code class="language-yaml"> - job_name: "synapse"
scrape_interval: 15s
metrics_path: "/_synapse/metrics"
static_configs:
- targets: ["my.server.here:port"]
labels:
instance: "my.server"
job: "master"
index: 1
- targets: ["my.workerserver.here:port"]
labels:
instance: "my.server"
job: "generic_worker"
index: 1
- targets: ["my.workerserver.here:port"]
labels:
instance: "my.server"
job: "generic_worker"
index: 2
- targets: ["my.workerserver.here:port"]
labels:
instance: "my.server"
job: "media_repository"
index: 1
</code></pre>
<p>Labels (<code>instance</code>, <code>job</code>, <code>index</code>) can be defined as anything.
The labels are used to group graphs in grafana.</p>
<h2 id="renaming-of-metrics--deprecation-of-old-names-in-12"><a class="header" href="#renaming-of-metrics--deprecation-of-old-names-in-12">Renaming of metrics & deprecation of old names in 1.2</a></h2>
<p>Synapse 1.2 updates the Prometheus metrics to match the naming
convention of the upstream <code>prometheus_client</code>. The old names are
considered deprecated and will be removed in a future version of
Synapse.</p>
<table><thead><tr><th>New Name</th><th>Old Name</th></tr></thead><tbody>
<tr><td>python_gc_objects_collected_total</td><td>python_gc_objects_collected</td></tr>
<tr><td>python_gc_objects_uncollectable_total</td><td>python_gc_objects_uncollectable</td></tr>
<tr><td>python_gc_collections_total</td><td>python_gc_collections</td></tr>
<tr><td>process_cpu_seconds_total</td><td>process_cpu_seconds</td></tr>
<tr><td>synapse_federation_client_sent_transactions_total</td><td>synapse_federation_client_sent_transactions</td></tr>
<tr><td>synapse_federation_client_events_processed_total</td><td>synapse_federation_client_events_processed</td></tr>
<tr><td>synapse_event_processing_loop_count_total</td><td>synapse_event_processing_loop_count</td></tr>
<tr><td>synapse_event_processing_loop_room_count_total</td><td>synapse_event_processing_loop_room_count</td></tr>
<tr><td>synapse_util_metrics_block_count_total</td><td>synapse_util_metrics_block_count</td></tr>
<tr><td>synapse_util_metrics_block_time_seconds_total</td><td>synapse_util_metrics_block_time_seconds</td></tr>
<tr><td>synapse_util_metrics_block_ru_utime_seconds_total</td><td>synapse_util_metrics_block_ru_utime_seconds</td></tr>
<tr><td>synapse_util_metrics_block_ru_stime_seconds_total</td><td>synapse_util_metrics_block_ru_stime_seconds</td></tr>
<tr><td>synapse_util_metrics_block_db_txn_count_total</td><td>synapse_util_metrics_block_db_txn_count</td></tr>
<tr><td>synapse_util_metrics_block_db_txn_duration_seconds_total</td><td>synapse_util_metrics_block_db_txn_duration_seconds</td></tr>
<tr><td>synapse_util_metrics_block_db_sched_duration_seconds_total</td><td>synapse_util_metrics_block_db_sched_duration_seconds</td></tr>
<tr><td>synapse_background_process_start_count_total</td><td>synapse_background_process_start_count</td></tr>
<tr><td>synapse_background_process_ru_utime_seconds_total</td><td>synapse_background_process_ru_utime_seconds</td></tr>
<tr><td>synapse_background_process_ru_stime_seconds_total</td><td>synapse_background_process_ru_stime_seconds</td></tr>
<tr><td>synapse_background_process_db_txn_count_total</td><td>synapse_background_process_db_txn_count</td></tr>
<tr><td>synapse_background_process_db_txn_duration_seconds_total</td><td>synapse_background_process_db_txn_duration_seconds</td></tr>
<tr><td>synapse_background_process_db_sched_duration_seconds_total</td><td>synapse_background_process_db_sched_duration_seconds</td></tr>
<tr><td>synapse_storage_events_persisted_events_total</td><td>synapse_storage_events_persisted_events</td></tr>
<tr><td>synapse_storage_events_persisted_events_sep_total</td><td>synapse_storage_events_persisted_events_sep</td></tr>
<tr><td>synapse_storage_events_state_delta_total</td><td>synapse_storage_events_state_delta</td></tr>
<tr><td>synapse_storage_events_state_delta_single_event_total</td><td>synapse_storage_events_state_delta_single_event</td></tr>
<tr><td>synapse_storage_events_state_delta_reuse_delta_total</td><td>synapse_storage_events_state_delta_reuse_delta</td></tr>
<tr><td>synapse_federation_server_received_pdus_total</td><td>synapse_federation_server_received_pdus</td></tr>
<tr><td>synapse_federation_server_received_edus_total</td><td>synapse_federation_server_received_edus</td></tr>
<tr><td>synapse_handler_presence_notified_presence_total</td><td>synapse_handler_presence_notified_presence</td></tr>
<tr><td>synapse_handler_presence_federation_presence_out_total</td><td>synapse_handler_presence_federation_presence_out</td></tr>
<tr><td>synapse_handler_presence_presence_updates_total</td><td>synapse_handler_presence_presence_updates</td></tr>
<tr><td>synapse_handler_presence_timers_fired_total</td><td>synapse_handler_presence_timers_fired</td></tr>
<tr><td>synapse_handler_presence_federation_presence_total</td><td>synapse_handler_presence_federation_presence</td></tr>
<tr><td>synapse_handler_presence_bump_active_time_total</td><td>synapse_handler_presence_bump_active_time</td></tr>
<tr><td>synapse_federation_client_sent_edus_total</td><td>synapse_federation_client_sent_edus</td></tr>
<tr><td>synapse_federation_client_sent_pdu_destinations_count_total</td><td>synapse_federation_client_sent_pdu_destinations:count</td></tr>
<tr><td>synapse_federation_client_sent_pdu_destinations_total</td><td>synapse_federation_client_sent_pdu_destinations:total</td></tr>
<tr><td>synapse_handlers_appservice_events_processed_total</td><td>synapse_handlers_appservice_events_processed</td></tr>
<tr><td>synapse_notifier_notified_events_total</td><td>synapse_notifier_notified_events</td></tr>
<tr><td>synapse_push_bulk_push_rule_evaluator_push_rules_invalidation_counter_total</td><td>synapse_push_bulk_push_rule_evaluator_push_rules_invalidation_counter</td></tr>
<tr><td>synapse_push_bulk_push_rule_evaluator_push_rules_state_size_counter_total</td><td>synapse_push_bulk_push_rule_evaluator_push_rules_state_size_counter</td></tr>
<tr><td>synapse_http_httppusher_http_pushes_processed_total</td><td>synapse_http_httppusher_http_pushes_processed</td></tr>
<tr><td>synapse_http_httppusher_http_pushes_failed_total</td><td>synapse_http_httppusher_http_pushes_failed</td></tr>
<tr><td>synapse_http_httppusher_badge_updates_processed_total</td><td>synapse_http_httppusher_badge_updates_processed</td></tr>
<tr><td>synapse_http_httppusher_badge_updates_failed_total</td><td>synapse_http_httppusher_badge_updates_failed</td></tr>
</tbody></table>
<h2 id="removal-of-deprecated-metrics--time-based-counters-becoming-histograms-in-0310"><a class="header" href="#removal-of-deprecated-metrics--time-based-counters-becoming-histograms-in-0310">Removal of deprecated metrics & time based counters becoming histograms in 0.31.0</a></h2>
<p>The duplicated metrics deprecated in Synapse 0.27.0 have been removed.</p>
<p>All time duration-based metrics have been changed to be seconds. This
affects:</p>
<table><thead><tr><th>msec -> sec metrics</th></tr></thead><tbody>
<tr><td>python_gc_time</td></tr>
<tr><td>python_twisted_reactor_tick_time</td></tr>
<tr><td>synapse_storage_query_time</td></tr>
<tr><td>synapse_storage_schedule_time</td></tr>
<tr><td>synapse_storage_transaction_time</td></tr>
</tbody></table>
<p>Several metrics have been changed to be histograms, which sort entries
into buckets and allow better analysis. The following metrics are now
histograms:</p>
<table><thead><tr><th>Altered metrics</th></tr></thead><tbody>
<tr><td>python_gc_time</td></tr>
<tr><td>python_twisted_reactor_pending_calls</td></tr>
<tr><td>python_twisted_reactor_tick_time</td></tr>
<tr><td>synapse_http_server_response_time_seconds</td></tr>
<tr><td>synapse_storage_query_time</td></tr>
<tr><td>synapse_storage_schedule_time</td></tr>
<tr><td>synapse_storage_transaction_time</td></tr>
</tbody></table>
<h2 id="block-and-response-metrics-renamed-for-0270"><a class="header" href="#block-and-response-metrics-renamed-for-0270">Block and response metrics renamed for 0.27.0</a></h2>
<p>Synapse 0.27.0 begins the process of rationalising the duplicate
<code>*:count</code> metrics reported for the resource tracking for code blocks and
HTTP requests.</p>
<p>At the same time, the corresponding <code>*:total</code> metrics are being renamed,
as the <code>:total</code> suffix no longer makes sense in the absence of a
corresponding <code>:count</code> metric.</p>
<p>To enable a graceful migration path, this release just adds new names
for the metrics being renamed. A future release will remove the old
ones.</p>
<p>The following table shows the new metrics, and the old metrics which
they are replacing.</p>
<table><thead><tr><th>New name</th><th>Old name</th></tr></thead><tbody>
<tr><td>synapse_util_metrics_block_count</td><td>synapse_util_metrics_block_timer:count</td></tr>
<tr><td>synapse_util_metrics_block_count</td><td>synapse_util_metrics_block_ru_utime:count</td></tr>
<tr><td>synapse_util_metrics_block_count</td><td>synapse_util_metrics_block_ru_stime:count</td></tr>
<tr><td>synapse_util_metrics_block_count</td><td>synapse_util_metrics_block_db_txn_count:count</td></tr>
<tr><td>synapse_util_metrics_block_count</td><td>synapse_util_metrics_block_db_txn_duration:count</td></tr>
<tr><td>synapse_util_metrics_block_time_seconds</td><td>synapse_util_metrics_block_timer:total</td></tr>
<tr><td>synapse_util_metrics_block_ru_utime_seconds</td><td>synapse_util_metrics_block_ru_utime:total</td></tr>
<tr><td>synapse_util_metrics_block_ru_stime_seconds</td><td>synapse_util_metrics_block_ru_stime:total</td></tr>
<tr><td>synapse_util_metrics_block_db_txn_count</td><td>synapse_util_metrics_block_db_txn_count:total</td></tr>
<tr><td>synapse_util_metrics_block_db_txn_duration_seconds</td><td>synapse_util_metrics_block_db_txn_duration:total</td></tr>
<tr><td>synapse_http_server_response_count</td><td>synapse_http_server_requests</td></tr>
<tr><td>synapse_http_server_response_count</td><td>synapse_http_server_response_time:count</td></tr>
<tr><td>synapse_http_server_response_count</td><td>synapse_http_server_response_ru_utime:count</td></tr>
<tr><td>synapse_http_server_response_count</td><td>synapse_http_server_response_ru_stime:count</td></tr>
<tr><td>synapse_http_server_response_count</td><td>synapse_http_server_response_db_txn_count:count</td></tr>
<tr><td>synapse_http_server_response_count</td><td>synapse_http_server_response_db_txn_duration:count</td></tr>
<tr><td>synapse_http_server_response_time_seconds</td><td>synapse_http_server_response_time:total</td></tr>
<tr><td>synapse_http_server_response_ru_utime_seconds</td><td>synapse_http_server_response_ru_utime:total</td></tr>
<tr><td>synapse_http_server_response_ru_stime_seconds</td><td>synapse_http_server_response_ru_stime:total</td></tr>
<tr><td>synapse_http_server_response_db_txn_count</td><td>synapse_http_server_response_db_txn_count:total</td></tr>
<tr><td>synapse_http_server_response_db_txn_duration_seconds</td><td>synapse_http_server_response_db_txn_duration:total</td></tr>
</tbody></table>
<h2 id="standard-metric-names"><a class="header" href="#standard-metric-names">Standard Metric Names</a></h2>
<p>As of synapse version 0.18.2, the format of the process-wide metrics has
been changed to fit prometheus standard naming conventions. Additionally
the units have been changed to seconds, from miliseconds.</p>
<table><thead><tr><th>New name</th><th>Old name</th></tr></thead><tbody>
<tr><td>process_cpu_user_seconds_total</td><td>process_resource_utime / 1000</td></tr>
<tr><td>process_cpu_system_seconds_total</td><td>process_resource_stime / 1000</td></tr>
<tr><td>process_open_fds (no 'type' label)</td><td>process_fds</td></tr>
</tbody></table>
<p>The python-specific counts of garbage collector performance have been
renamed.</p>
<table><thead><tr><th>New name</th><th>Old name</th></tr></thead><tbody>
<tr><td>python_gc_time</td><td>reactor_gc_time</td></tr>
<tr><td>python_gc_unreachable_total</td><td>reactor_gc_unreachable</td></tr>
<tr><td>python_gc_counts</td><td>reactor_gc_counts</td></tr>
</tbody></table>
<p>The twisted-specific reactor metrics have been renamed.</p>
<table><thead><tr><th>New name</th><th>Old name</th></tr></thead><tbody>
<tr><td>python_twisted_reactor_pending_calls</td><td>reactor_pending_calls</td></tr>
<tr><td>python_twisted_reactor_tick_time</td><td>reactor_tick_time</td></tr>
</tbody></table>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="request-log-format"><a class="header" href="#request-log-format">Request log format</a></h1>
<p>HTTP request logs are written by synapse (see <a href="usage/administration/../synapse/http/site.py"><code>site.py</code></a> for details).</p>
<p>See the following for how to decode the dense data available from the default logging configuration.</p>
<pre><code>2020-10-01 12:00:00,000 - synapse.access.http.8008 - 311 - INFO - PUT-1000- 192.168.0.1 - 8008 - {another-matrix-server.com} Processed request: 0.100sec/-0.000sec (0.000sec, 0.000sec) (0.001sec/0.090sec/3) 11B !200 "PUT /_matrix/federation/v1/send/1600000000000 HTTP/1.1" "Synapse/1.20.1" [0 dbevts]
-AAAAAAAAAAAAAAAAAAAAA- -BBBBBBBBBBBBBBBBBBBBBB- -C- -DD- -EEEEEE- -FFFFFFFFF- -GG- -HHHHHHHHHHHHHHHHHHHHHHH- -IIIIII- -JJJJJJJ- -KKKKKK-, -LLLLLL- -MMMMMMM- -NNNNNN- O -P- -QQ- -RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR- -SSSSSSSSSSSS- -TTTTTT-
</code></pre>
<table><thead><tr><th>Part</th><th>Explanation</th></tr></thead><tbody>
<tr><td>AAAA</td><td>Timestamp request was logged (not recieved)</td></tr>
<tr><td>BBBB</td><td>Logger name (<code>synapse.access.(http\|https).<tag></code>, where 'tag' is defined in the <code>listeners</code> config section, normally the port)</td></tr>
<tr><td>CCCC</td><td>Line number in code</td></tr>
<tr><td>DDDD</td><td>Log Level</td></tr>
<tr><td>EEEE</td><td>Request Identifier (This identifier is shared by related log lines)</td></tr>
<tr><td>FFFF</td><td>Source IP (Or X-Forwarded-For if enabled)</td></tr>
<tr><td>GGGG</td><td>Server Port</td></tr>
<tr><td>HHHH</td><td>Federated Server or Local User making request (blank if unauthenticated or not supplied)</td></tr>
<tr><td>IIII</td><td>Total Time to process the request</td></tr>
<tr><td>JJJJ</td><td>Time to send response over network once generated (this may be negative if the socket is closed before the response is generated)</td></tr>
<tr><td>KKKK</td><td>Userland CPU time</td></tr>
<tr><td>LLLL</td><td>System CPU time</td></tr>
<tr><td>MMMM</td><td>Total time waiting for a free DB connection from the pool across all parallel DB work from this request</td></tr>
<tr><td>NNNN</td><td>Total time waiting for response to DB queries across all parallel DB work from this request</td></tr>
<tr><td>OOOO</td><td>Count of DB transactions performed</td></tr>
<tr><td>PPPP</td><td>Response body size</td></tr>
<tr><td>QQQQ</td><td>Response status code (prefixed with ! if the socket was closed before the response was generated)</td></tr>
<tr><td>RRRR</td><td>Request</td></tr>
<tr><td>SSSS</td><td>User-agent</td></tr>
<tr><td>TTTT</td><td>Events fetched from DB to service this request (note that this does not include events fetched from the cache)</td></tr>
</tbody></table>
<p>MMMM / NNNN can be greater than IIII if there are multiple slow database queries
running in parallel.</p>
<p>Some actions can result in multiple identical http requests, which will return
the same data, but only the first request will report time/transactions in
<code>KKKK</code>/<code>LLLL</code>/<code>MMMM</code>/<code>NNNN</code>/<code>OOOO</code> - the others will be awaiting the first query to return a
response and will simultaneously return with the first request, but with very
small processing times.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><!--
Include the contents of CONTRIBUTING.md from the project root (where GitHub likes it
to be)
-->
<h1 id="contributing"><a class="header" href="#contributing">Contributing</a></h1>
<p>Welcome to Synapse</p>
<p>This document aims to get you started with contributing to this repo! </p>
<ul>
<li><a href="development/contributing_guide.html#1-who-can-contribute-to-synapse">1. Who can contribute to Synapse?</a></li>
<li><a href="development/contributing_guide.html#2-what-do-i-need">2. What do I need?</a></li>
<li><a href="development/contributing_guide.html#3-get-the-source">3. Get the source.</a></li>
<li><a href="development/contributing_guide.html#4-install-the-dependencies">4. Install the dependencies</a>
<ul>
<li><a href="development/contributing_guide.html#under-unix-macos-linux-bsd-">Under Unix (macOS, Linux, BSD, ...)</a></li>
<li><a href="development/contributing_guide.html#under-windows">Under Windows</a></li>
</ul>
</li>
<li><a href="development/contributing_guide.html#5-get-in-touch">5. Get in touch.</a></li>
<li><a href="development/contributing_guide.html#6-pick-an-issue">6. Pick an issue.</a></li>
<li><a href="development/contributing_guide.html#7-turn-coffee-and-documentation-into-code-and-documentation">7. Turn coffee and documentation into code and documentation!</a></li>
<li><a href="development/contributing_guide.html#8-test-test-test">8. Test, test, test!</a>
<ul>
<li><a href="development/contributing_guide.html#run-the-linters">Run the linters.</a></li>
<li><a href="development/contributing_guide.html#run-the-unit-tests">Run the unit tests.</a></li>
<li><a href="development/contributing_guide.html#run-the-integration-tests">Run the integration tests.</a></li>
</ul>
</li>
<li><a href="development/contributing_guide.html#9-submit-your-patch">9. Submit your patch.</a>
<ul>
<li><a href="development/contributing_guide.html#changelog">Changelog</a>
<ul>
<li><a href="development/contributing_guide.html#how-do-i-know-what-to-call-the-changelog-file-before-i-create-the-pr">How do I know what to call the changelog file before I create the PR?</a></li>
<li><a href="development/contributing_guide.html#debian-changelog">Debian changelog</a></li>
</ul>
</li>
<li><a href="development/contributing_guide.html#sign-off">Sign off</a></li>
</ul>
</li>
<li><a href="development/contributing_guide.html#10-turn-feedback-into-better-code">10. Turn feedback into better code.</a></li>
<li><a href="development/contributing_guide.html#11-find-a-new-issue">11. Find a new issue.</a></li>
<li><a href="development/contributing_guide.html#notes-for-maintainers-on-merging-prs-etc">Notes for maintainers on merging PRs etc</a></li>
<li><a href="development/contributing_guide.html#conclusion">Conclusion</a></li>
</ul>
<h1 id="1-who-can-contribute-to-synapse"><a class="header" href="#1-who-can-contribute-to-synapse">1. Who can contribute to Synapse?</a></h1>
<p>Everyone is welcome to contribute code to <a href="https://github.com/matrix-org">matrix.org
projects</a>, provided that they are willing to
license their contributions under the same license as the project itself. We
follow a simple 'inbound=outbound' model for contributions: the act of
submitting an 'inbound' contribution means that the contributor agrees to
license the code under the same terms as the project's overall 'outbound'
license - in our case, this is almost always Apache Software License v2 (see
<a href="development/LICENSE">LICENSE</a>).</p>
<h1 id="2-what-do-i-need"><a class="header" href="#2-what-do-i-need">2. What do I need?</a></h1>
<p>The code of Synapse is written in Python 3. To do pretty much anything, you'll need <a href="https://wiki.python.org/moin/BeginnersGuide/Download">a recent version of Python 3</a>.</p>
<p>The source code of Synapse is hosted on GitHub. You will also need <a href="https://github.com/git-guides/install-git">a recent version of git</a>.</p>
<p>For some tests, you will need <a href="https://docs.docker.com/get-docker/">a recent version of Docker</a>.</p>
<h1 id="3-get-the-source"><a class="header" href="#3-get-the-source">3. Get the source.</a></h1>
<p>The preferred and easiest way to contribute changes is to fork the relevant
project on GitHub, and then <a href="https://help.github.com/articles/using-pull-requests/">create a pull request</a> to ask us to pull your
changes into our repo.</p>
<p>Please base your changes on the <code>develop</code> branch.</p>
<pre><code class="language-sh">git clone git@github.com:YOUR_GITHUB_USER_NAME/synapse.git
git checkout develop
</code></pre>
<p>If you need help getting started with git, this is beyond the scope of the document, but you
can find many good git tutorials on the web.</p>
<h1 id="4-install-the-dependencies"><a class="header" href="#4-install-the-dependencies">4. Install the dependencies</a></h1>
<h2 id="under-unix-macos-linux-bsd-"><a class="header" href="#under-unix-macos-linux-bsd-">Under Unix (macOS, Linux, BSD, ...)</a></h2>
<p>Once you have installed Python 3 and added the source, please open a terminal and
setup a <em>virtualenv</em>, as follows:</p>
<pre><code class="language-sh">cd path/where/you/have/cloned/the/repository
python3 -m venv ./env
source ./env/bin/activate
pip install -e ".[all,lint,mypy,test]"
pip install tox
</code></pre>
<p>This will install the developer dependencies for the project.</p>
<h2 id="under-windows"><a class="header" href="#under-windows">Under Windows</a></h2>
<p>TBD</p>
<h1 id="5-get-in-touch"><a class="header" href="#5-get-in-touch">5. Get in touch.</a></h1>
<p>Join our developer community on Matrix: #synapse-dev:matrix.org !</p>
<h1 id="6-pick-an-issue"><a class="header" href="#6-pick-an-issue">6. Pick an issue.</a></h1>
<p>Fix your favorite problem or perhaps find a <a href="https://github.com/matrix-org/synapse/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+First+Issue%22">Good First Issue</a>
to work on.</p>
<h1 id="7-turn-coffee-and-documentation-into-code-and-documentation"><a class="header" href="#7-turn-coffee-and-documentation-into-code-and-documentation">7. Turn coffee and documentation into code and documentation!</a></h1>
<p>Synapse's code style is documented <a href="development/docs/code_style.html">here</a>. Please follow
it, including the conventions for the <a href="development/docs/code_style.html#configuration-file-format">sample configuration
file</a>.</p>
<p>There is a growing amount of documentation located in the <a href="development/docs">docs</a>
directory. This documentation is intended primarily for sysadmins running their
own Synapse instance, as well as developers interacting externally with
Synapse. <a href="development/docs/dev">docs/dev</a> exists primarily to house documentation for
Synapse developers. <a href="development/docs/admin_api">docs/admin_api</a> houses documentation
regarding Synapse's Admin API, which is used mostly by sysadmins and external
service developers.</p>
<p>If you add new files added to either of these folders, please use <a href="https://guides.github.com/features/mastering-markdown/">GitHub-Flavoured
Markdown</a>.</p>
<p>Some documentation also exists in <a href="https://github.com/matrix-org/synapse/wiki">Synapse's GitHub
Wiki</a>, although this is primarily
contributed to by community authors.</p>
<h1 id="8-test-test-test"><a class="header" href="#8-test-test-test">8. Test, test, test!</a></h1>
<p><a name="test-test-test"></a></p>
<p>While you're developing and before submitting a patch, you'll
want to test your code.</p>
<h2 id="run-the-linters"><a class="header" href="#run-the-linters">Run the linters.</a></h2>
<p>The linters look at your code and do two things:</p>
<ul>
<li>ensure that your code follows the coding style adopted by the project;</li>
<li>catch a number of errors in your code.</li>
</ul>
<p>They're pretty fast, don't hesitate!</p>
<pre><code class="language-sh">source ./env/bin/activate
./scripts-dev/lint.sh
</code></pre>
<p>Note that this script <em>will modify your files</em> to fix styling errors.
Make sure that you have saved all your files.</p>
<p>If you wish to restrict the linters to only the files changed since the last commit
(much faster!), you can instead run:</p>
<pre><code class="language-sh">source ./env/bin/activate
./scripts-dev/lint.sh -d
</code></pre>
<p>Or if you know exactly which files you wish to lint, you can instead run:</p>
<pre><code class="language-sh">source ./env/bin/activate
./scripts-dev/lint.sh path/to/file1.py path/to/file2.py path/to/folder
</code></pre>
<h2 id="run-the-unit-tests"><a class="header" href="#run-the-unit-tests">Run the unit tests.</a></h2>
<p>The unit tests run parts of Synapse, including your changes, to see if anything
was broken. They are slower than the linters but will typically catch more errors.</p>
<pre><code class="language-sh">source ./env/bin/activate
trial tests
</code></pre>
<p>If you wish to only run <em>some</em> unit tests, you may specify
another module instead of <code>tests</code> - or a test class or a method:</p>
<pre><code class="language-sh">source ./env/bin/activate
trial tests.rest.admin.test_room tests.handlers.test_admin.ExfiltrateData.test_invite
</code></pre>
<p>If your tests fail, you may wish to look at the logs (the default log level is <code>ERROR</code>):</p>
<pre><code class="language-sh">less _trial_temp/test.log
</code></pre>
<p>To increase the log level for the tests, set <code>SYNAPSE_TEST_LOG_LEVEL</code>:</p>
<pre><code class="language-sh">SYNAPSE_TEST_LOG_LEVEL=DEBUG trial tests
</code></pre>
<h2 id="run-the-integration-tests"><a class="header" href="#run-the-integration-tests">Run the integration tests.</a></h2>
<p>The integration tests are a more comprehensive suite of tests. They
run a full version of Synapse, including your changes, to check if
anything was broken. They are slower than the unit tests but will
typically catch more errors.</p>
<p>The following command will let you run the integration test with the most common
configuration:</p>
<pre><code class="language-sh">$ docker run --rm -it -v /path/where/you/have/cloned/the/repository\:/src:ro -v /path/to/where/you/want/logs\:/logs matrixdotorg/sytest-synapse:py37
</code></pre>
<p>This configuration should generally cover your needs. For more details about other configurations, see <a href="https://github.com/matrix-org/sytest/blob/develop/docker/README.md">documentation in the SyTest repo</a>.</p>
<h1 id="9-submit-your-patch"><a class="header" href="#9-submit-your-patch">9. Submit your patch.</a></h1>
<p>Once you're happy with your patch, it's time to prepare a Pull Request.</p>
<p>To prepare a Pull Request, please:</p>
<ol>
<li>verify that <a href="development/contributing_guide.html#test-test-test">all the tests pass</a>, including the coding style;</li>
<li><a href="development/contributing_guide.html#sign-off">sign off</a> your contribution;</li>
<li><code>git push</code> your commit to your fork of Synapse;</li>
<li>on GitHub, <a href="https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request">create the Pull Request</a>;</li>
<li>add a <a href="development/contributing_guide.html#changelog">changelog entry</a> and push it to your Pull Request;</li>
<li>for most contributors, that's all - however, if you are a member of the organization <code>matrix-org</code>, on GitHub, please request a review from <code>matrix.org / Synapse Core</code>.</li>
</ol>
<h2 id="changelog"><a class="header" href="#changelog">Changelog</a></h2>
<p>All changes, even minor ones, need a corresponding changelog / newsfragment
entry. These are managed by <a href="https://github.com/hawkowl/towncrier">Towncrier</a>.</p>
<p>To create a changelog entry, make a new file in the <code>changelog.d</code> directory named
in the format of <code>PRnumber.type</code>. The type can be one of the following:</p>
<ul>
<li><code>feature</code></li>
<li><code>bugfix</code></li>
<li><code>docker</code> (for updates to the Docker image)</li>
<li><code>doc</code> (for updates to the documentation)</li>
<li><code>removal</code> (also used for deprecations)</li>
<li><code>misc</code> (for internal-only changes)</li>
</ul>
<p>This file will become part of our <a href="https://github.com/matrix-org/synapse/blob/master/CHANGES.md">changelog</a> at the next
release, so the content of the file should be a short description of your
change in the same style as the rest of the changelog. The file can contain Markdown
formatting, and should end with a full stop (.) or an exclamation mark (!) for
consistency.</p>
<p>Adding credits to the changelog is encouraged, we value your
contributions and would like to have you shouted out in the release notes!</p>
<p>For example, a fix in PR #1234 would have its changelog entry in
<code>changelog.d/1234.bugfix</code>, and contain content like:</p>
<blockquote>
<p>The security levels of Florbs are now validated when received
via the <code>/federation/florb</code> endpoint. Contributed by Jane Matrix.</p>
</blockquote>
<p>If there are multiple pull requests involved in a single bugfix/feature/etc,
then the content for each <code>changelog.d</code> file should be the same. Towncrier will
merge the matching files together into a single changelog entry when we come to
release.</p>
<h3 id="how-do-i-know-what-to-call-the-changelog-file-before-i-create-the-pr"><a class="header" href="#how-do-i-know-what-to-call-the-changelog-file-before-i-create-the-pr">How do I know what to call the changelog file before I create the PR?</a></h3>
<p>Obviously, you don't know if you should call your newsfile
<code>1234.bugfix</code> or <code>5678.bugfix</code> until you create the PR, which leads to a
chicken-and-egg problem.</p>
<p>There are two options for solving this:</p>
<ol>
<li>
<p>Open the PR without a changelog file, see what number you got, and <em>then</em>
add the changelog file to your branch (see <a href="development/contributing_guide.html#updating-your-pull-request">Updating your pull
request</a>), or:</p>
</li>
<li>
<p>Look at the <a href="https://github.com/matrix-org/synapse/issues?q=">list of all
issues/PRs</a>, add one to the
highest number you see, and quickly open the PR before somebody else claims
your number.</p>
<p><a href="https://github.com/richvdh/scripts/blob/master/next_github_number.sh">This
script</a>
might be helpful if you find yourself doing this a lot.</p>
</li>
</ol>
<p>Sorry, we know it's a bit fiddly, but it's <em>really</em> helpful for us when we come
to put together a release!</p>
<h3 id="debian-changelog"><a class="header" href="#debian-changelog">Debian changelog</a></h3>
<p>Changes which affect the debian packaging files (in <code>debian</code>) are an
exception to the rule that all changes require a <code>changelog.d</code> file.</p>
<p>In this case, you will need to add an entry to the debian changelog for the
next release. For this, run the following command:</p>
<pre><code>dch
</code></pre>
<p>This will make up a new version number (if there isn't already an unreleased
version in flight), and open an editor where you can add a new changelog entry.
(Our release process will ensure that the version number and maintainer name is
corrected for the release.)</p>
<p>If your change affects both the debian packaging <em>and</em> files outside the debian
directory, you will need both a regular newsfragment <em>and</em> an entry in the
debian changelog. (Though typically such changes should be submitted as two
separate pull requests.)</p>
<h2 id="sign-off"><a class="header" href="#sign-off">Sign off</a></h2>
<p>In order to have a concrete record that your contribution is intentional
and you agree to license it under the same terms as the project's license, we've adopted the
same lightweight approach that the Linux Kernel
<a href="https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin%3E">submitting patches process</a>,
<a href="https://github.com/docker/docker/blob/master/CONTRIBUTING.md">Docker</a>, and many other
projects use: the DCO (Developer Certificate of Origin:
http://developercertificate.org/). This is a simple declaration that you wrote
the contribution or otherwise have the right to contribute it to Matrix:</p>
<pre><code>Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
</code></pre>
<p>If you agree to this for your contribution, then all that's needed is to
include the line in your commit or pull request comment:</p>
<pre><code>Signed-off-by: Your Name <your@email.example.org>
</code></pre>
<p>We accept contributions under a legally identifiable name, such as
your name on government documentation or common-law names (names
claimed by legitimate usage or repute). Unfortunately, we cannot
accept anonymous contributions at this time.</p>
<p>Git allows you to add this signoff automatically when using the <code>-s</code>
flag to <code>git commit</code>, which uses the name and email set in your
<code>user.name</code> and <code>user.email</code> git configs.</p>
<h1 id="10-turn-feedback-into-better-code"><a class="header" href="#10-turn-feedback-into-better-code">10. Turn feedback into better code.</a></h1>
<p>Once the Pull Request is opened, you will see a few things:</p>
<ol>
<li>our automated CI (Continuous Integration) pipeline will run (again) the linters, the unit tests, the integration tests and more;</li>
<li>one or more of the developers will take a look at your Pull Request and offer feedback.</li>
</ol>
<p>From this point, you should:</p>
<ol>
<li>Look at the results of the CI pipeline.
<ul>
<li>If there is any error, fix the error.</li>
</ul>
</li>
<li>If a developer has requested changes, make these changes and let us know if it is ready for a developer to review again.</li>
<li>Create a new commit with the changes.
<ul>
<li>Please do NOT overwrite the history. New commits make the reviewer's life easier.</li>
<li>Push this commits to your Pull Request.</li>
</ul>
</li>
<li>Back to 1.</li>
</ol>
<p>Once both the CI and the developers are happy, the patch will be merged into Synapse and released shortly!</p>
<h1 id="11-find-a-new-issue"><a class="header" href="#11-find-a-new-issue">11. Find a new issue.</a></h1>
<p>By now, you know the drill!</p>
<h1 id="notes-for-maintainers-on-merging-prs-etc"><a class="header" href="#notes-for-maintainers-on-merging-prs-etc">Notes for maintainers on merging PRs etc</a></h1>
<p>There are some notes for those with commit access to the project on how we
manage git <a href="development/docs/development/git.html">here</a>.</p>
<h1 id="conclusion"><a class="header" href="#conclusion">Conclusion</a></h1>
<p>That's it! Matrix is a very open and collaborative project as you might expect
given our obsession with open communication. If we're going to successfully
matrix together all the fragmented communication technologies out there we are
reliant on contributions and collaboration from the community to do so. So
please get involved - and we hope you have as much fun hacking on Matrix as we
do!</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="code-style"><a class="header" href="#code-style">Code Style</a></h1>
<h2 id="formatting-tools"><a class="header" href="#formatting-tools">Formatting tools</a></h2>
<p>The Synapse codebase uses a number of code formatting tools in order to
quickly and automatically check for formatting (and sometimes logical)
errors in code.</p>
<p>The necessary tools are detailed below.</p>
<p>First install them with:</p>
<pre><code>pip install -e ".[lint,mypy]"
</code></pre>
<ul>
<li>
<p><strong>black</strong></p>
<p>The Synapse codebase uses <a href="https://pypi.org/project/black/">black</a>
as an opinionated code formatter, ensuring all comitted code is
properly formatted.</p>
<p>Have <code>black</code> auto-format your code (it shouldn't change any
functionality) with:</p>
<pre><code>black . --exclude="\.tox|build|env"
</code></pre>
</li>
<li>
<p><strong>flake8</strong></p>
<p><code>flake8</code> is a code checking tool. We require code to pass <code>flake8</code>
before being merged into the codebase.</p>
<p>Check all application and test code with:</p>
<pre><code>flake8 synapse tests
</code></pre>
</li>
<li>
<p><strong>isort</strong></p>
<p><code>isort</code> ensures imports are nicely formatted, and can suggest and
auto-fix issues such as double-importing.</p>
<p>Auto-fix imports with:</p>
<pre><code>isort -rc synapse tests
</code></pre>
<p><code>-rc</code> means to recursively search the given directories.</p>
</li>
</ul>
<p>It's worth noting that modern IDEs and text editors can run these tools
automatically on save. It may be worth looking into whether this
functionality is supported in your editor for a more convenient
development workflow. It is not, however, recommended to run <code>flake8</code> on
save as it takes a while and is very resource intensive.</p>
<h2 id="general-rules"><a class="header" href="#general-rules">General rules</a></h2>
<ul>
<li><strong>Naming</strong>:
<ul>
<li>Use camel case for class and type names</li>
<li>Use underscores for functions and variables.</li>
</ul>
</li>
<li><strong>Docstrings</strong>: should follow the <a href="https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings">google code
style</a>.
See the
<a href="http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html">examples</a>
in the sphinx documentation.</li>
<li><strong>Imports</strong>:
<ul>
<li>
<p>Imports should be sorted by <code>isort</code> as described above.</p>
</li>
<li>
<p>Prefer to import classes and functions rather than packages or
modules.</p>
<p>Example:</p>
<pre><code>from synapse.types import UserID
...
user_id = UserID(local, server)
</code></pre>
<p>is preferred over:</p>
<pre><code>from synapse import types
...
user_id = types.UserID(local, server)
</code></pre>
<p>(or any other variant).</p>
<p>This goes against the advice in the Google style guide, but it
means that errors in the name are caught early (at import time).</p>
</li>
<li>
<p>Avoid wildcard imports (<code>from synapse.types import *</code>) and
relative imports (<code>from .types import UserID</code>).</p>
</li>
</ul>
</li>
</ul>
<h2 id="configuration-file-format"><a class="header" href="#configuration-file-format">Configuration file format</a></h2>
<p>The <a href="./sample_config.yaml">sample configuration file</a> acts as a
reference to Synapse's configuration options for server administrators.
Remember that many readers will be unfamiliar with YAML and server
administration in general, so that it is important that the file be as
easy to understand as possible, which includes following a consistent
format.</p>
<p>Some guidelines follow:</p>
<ul>
<li>
<p>Sections should be separated with a heading consisting of a single
line prefixed and suffixed with <code>##</code>. There should be <strong>two</strong> blank
lines before the section header, and <strong>one</strong> after.</p>
</li>
<li>
<p>Each option should be listed in the file with the following format:</p>
<ul>
<li>
<p>A comment describing the setting. Each line of this comment
should be prefixed with a hash (<code>#</code>) and a space.</p>
<p>The comment should describe the default behaviour (ie, what
happens if the setting is omitted), as well as what the effect
will be if the setting is changed.</p>
<p>Often, the comment end with something like "uncomment the
following to <do action>".</p>
</li>
<li>
<p>A line consisting of only <code>#</code>.</p>
</li>
<li>
<p>A commented-out example setting, prefixed with only <code>#</code>.</p>
<p>For boolean (on/off) options, convention is that this example
should be the <em>opposite</em> to the default (so the comment will end
with "Uncomment the following to enable [or disable]
<feature>." For other options, the example should give some
non-default value which is likely to be useful to the reader.</p>
</li>
</ul>
</li>
<li>
<p>There should be a blank line between each option.</p>
</li>
<li>
<p>Where several settings are grouped into a single dict, <em>avoid</em> the
convention where the whole block is commented out, resulting in
comment lines starting <code># #</code>, as this is hard to read and confusing
to edit. Instead, leave the top-level config option uncommented, and
follow the conventions above for sub-options. Ensure that your code
correctly handles the top-level option being set to <code>None</code> (as it
will be if no sub-options are enabled).</p>
</li>
<li>
<p>Lines should be wrapped at 80 characters.</p>
</li>
<li>
<p>Use two-space indents.</p>
</li>
<li>
<p><code>true</code> and <code>false</code> are spelt thus (as opposed to <code>True</code>, etc.)</p>
</li>
<li>
<p>Use single quotes (<code>'</code>) rather than double-quotes (<code>"</code>) or backticks
(<code>`</code>) to refer to configuration options.</p>
</li>
</ul>
<p>Example:</p>
<pre><code>## Frobnication ##
# The frobnicator will ensure that all requests are fully frobnicated.
# To enable it, uncomment the following.
#
#frobnicator_enabled: true
# By default, the frobnicator will frobnicate with the default frobber.
# The following will make it use an alternative frobber.
#
#frobincator_frobber: special_frobber
# Settings for the frobber
#
frobber:
# frobbing speed. Defaults to 1.
#
#speed: 10
# frobbing distance. Defaults to 1000.
#
#distance: 100
</code></pre>
<p>Note that the sample configuration is generated from the synapse code
and is maintained by a script, <code>scripts-dev/generate_sample_config</code>.
Making sure that the output from this script matches the desired format
is left as an exercise for the reader!</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="some-notes-on-how-we-use-git"><a class="header" href="#some-notes-on-how-we-use-git">Some notes on how we use git</a></h1>
<h2 id="on-keeping-the-commit-history-clean"><a class="header" href="#on-keeping-the-commit-history-clean">On keeping the commit history clean</a></h2>
<p>In an ideal world, our git commit history would be a linear progression of
commits each of which contains a single change building on what came
before. Here, by way of an arbitrary example, is the top of <code>git log --graph b2dba0607</code>:</p>
<img src="development/img/git/clean.png" alt="clean git graph" width="500px">
<p>Note how the commit comment explains clearly what is changing and why. Also
note the <em>absence</em> of merge commits, as well as the absence of commits called
things like (to pick a few culprits):
<a href="https://github.com/matrix-org/synapse/commit/84691da6c">“pep8”</a>, <a href="https://github.com/matrix-org/synapse/commit/474810d9d">“fix broken
test”</a>,
<a href="https://github.com/matrix-org/synapse/commit/c9d72e457">“oops”</a>,
<a href="https://github.com/matrix-org/synapse/commit/836358823">“typo”</a>, or <a href="https://github.com/matrix-org/synapse/commit/707374d5d">“Who's
the president?”</a>.</p>
<p>There are a number of reasons why keeping a clean commit history is a good
thing:</p>
<ul>
<li>
<p>From time to time, after a change lands, it turns out to be necessary to
revert it, or to backport it to a release branch. Those operations are
<em>much</em> easier when the change is contained in a single commit.</p>
</li>
<li>
<p>Similarly, it's much easier to answer questions like “is the fix for
<code>/publicRooms</code> on the release branch?” if that change consists of a single
commit.</p>
</li>
<li>
<p>Likewise: “what has changed on this branch in the last week?” is much
clearer without merges and “pep8” commits everywhere.</p>
</li>
<li>
<p>Sometimes we need to figure out where a bug got introduced, or some
behaviour changed. One way of doing that is with <code>git bisect</code>: pick an
arbitrary commit between the known good point and the known bad point, and
see how the code behaves. However, that strategy fails if the commit you
chose is the middle of someone's epic branch in which they broke the world
before putting it back together again.</p>
</li>
</ul>
<p>One counterargument is that it is sometimes useful to see how a PR evolved as
it went through review cycles. This is true, but that information is always
available via the GitHub UI (or via the little-known <a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/checking-out-pull-requests-locally">refs/pull
namespace</a>).</p>
<p>Of course, in reality, things are more complicated than that. We have release
branches as well as <code>develop</code> and <code>master</code>, and we deliberately merge changes
between them. Bugs often slip through and have to be fixed later. That's all
fine: this not a cast-iron rule which must be obeyed, but an ideal to aim
towards.</p>
<h2 id="merges-squashes-rebases-wtf"><a class="header" href="#merges-squashes-rebases-wtf">Merges, squashes, rebases: wtf?</a></h2>
<p>Ok, so that's what we'd like to achieve. How do we achieve it?</p>
<p>The TL;DR is: when you come to merge a pull request, you <em>probably</em> want to
“squash and merge”:</p>
<p><img src="development/img/git/squash.png" alt="squash and merge" />.</p>
<p>(This applies whether you are merging your own PR, or that of another
contributor.)</p>
<p>“Squash and merge”<sup id="a1"><a href="development/git.html#f1">1</a></sup> takes all of the changes in the
PR, and bundles them into a single commit. GitHub gives you the opportunity to
edit the commit message before you confirm, and normally you should do so,
because the default will be useless (again: <code>* woops typo</code> is not a useful
thing to keep in the historical record).</p>
<p>The main problem with this approach comes when you have a series of pull
requests which build on top of one another: as soon as you squash-merge the
first PR, you'll end up with a stack of conflicts to resolve in all of the
others. In general, it's best to avoid this situation in the first place by
trying not to have multiple related PRs in flight at the same time. Still,
sometimes that's not possible and doing a regular merge is the lesser evil.</p>
<p>Another occasion in which a regular merge makes more sense is a PR where you've
deliberately created a series of commits each of which makes sense in its own
right. For example: <a href="https://github.com/matrix-org/synapse/pull/6837">a PR which gradually propagates a refactoring operation
through the codebase</a>, or <a href="https://github.com/matrix-org/synapse/pull/5987">a
PR which is the culmination of several other
PRs</a>. In this case the ability
to figure out when a particular change/bug was introduced could be very useful.</p>
<p>Ultimately: <strong>this is not a hard-and-fast-rule</strong>. If in doubt, ask yourself “do
each of the commits I am about to merge make sense in their own right”, but
remember that we're just doing our best to balance “keeping the commit history
clean” with other factors.</p>
<h2 id="git-branching-model"><a class="header" href="#git-branching-model">Git branching model</a></h2>
<p>A <a href="https://nvie.com/posts/a-successful-git-branching-model/">lot</a>
<a href="http://scottchacon.com/2011/08/31/github-flow.html">of</a>
<a href="https://www.endoflineblog.com/gitflow-considered-harmful">words</a> have been
written in the past about git branching models (no really, <a href="https://martinfowler.com/articles/branching-patterns.html">a
lot</a>). I tend to
think the whole thing is overblown. Fundamentally, it's not that
complicated. Here's how we do it.</p>
<p>Let's start with a picture:</p>
<p><img src="development/img/git/branches.jpg" alt="branching model" /></p>
<p>It looks complicated, but it's really not. There's one basic rule: <em>anyone</em> is
free to merge from <em>any</em> more-stable branch to <em>any</em> less-stable branch at
<em>any</em> time<sup id="a2"><a href="development/git.html#f2">2</a></sup>. (The principle behind this is that if a
change is good enough for the more-stable branch, then it's also good enough go
put in a less-stable branch.)</p>
<p>Meanwhile, merging (or squashing, as per the above) from a less-stable to a
more-stable branch is a deliberate action in which you want to publish a change
or a set of changes to (some subset of) the world: for example, this happens
when a PR is landed, or as part of our release process.</p>
<p>So, what counts as a more- or less-stable branch? A little reflection will show
that our active branches are ordered thus, from more-stable to less-stable:</p>
<ul>
<li><code>master</code> (tracks our last release).</li>
<li><code>release-vX.Y</code> (the branch where we prepare the next release)<sup
id="a3"><a href="development/git.html#f3">3</a></sup>.</li>
<li>PR branches which are targeting the release.</li>
<li><code>develop</code> (our "mainline" branch containing our bleeding-edge).</li>
<li>regular PR branches.</li>
</ul>
<p>The corollary is: if you have a bugfix that needs to land in both
<code>release-vX.Y</code> <em>and</em> <code>develop</code>, then you should base your PR on
<code>release-vX.Y</code>, get it merged there, and then merge from <code>release-vX.Y</code> to
<code>develop</code>. (If a fix lands in <code>develop</code> and we later need it in a
release-branch, we can of course cherry-pick it, but landing it in the release
branch first helps reduce the chance of annoying conflicts.)</p>
<hr />
<p><b id="f1">[1]</b>: “Squash and merge” is GitHub's term for this
operation. Given that there is no merge involved, I'm not convinced it's the
most intuitive name. <a href="development/git.html#a1">^</a></p>
<p><b id="f2">[2]</b>: Well, anyone with commit access.<a href="development/git.html#a2">^</a></p>
<p><b id="f3">[3]</b>: Very, very occasionally (I think this has happened once in
the history of Synapse), we've had two releases in flight at once. Obviously,
<code>release-v1.2</code> is more-stable than <code>release-v1.3</code>. <a href="development/git.html#a3">^</a></p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="opentracing"><a class="header" href="#opentracing">OpenTracing</a></h1>
<h2 id="background"><a class="header" href="#background">Background</a></h2>
<p>OpenTracing is a semi-standard being adopted by a number of distributed
tracing platforms. It is a common api for facilitating vendor-agnostic
tracing instrumentation. That is, we can use the OpenTracing api and
select one of a number of tracer implementations to do the heavy lifting
in the background. Our current selected implementation is Jaeger.</p>
<p>OpenTracing is a tool which gives an insight into the causal
relationship of work done in and between servers. The servers each track
events and report them to a centralised server - in Synapse's case:
Jaeger. The basic unit used to represent events is the span. The span
roughly represents a single piece of work that was done and the time at
which it occurred. A span can have child spans, meaning that the work of
the child had to be completed for the parent span to complete, or it can
have follow-on spans which represent work that is undertaken as a result
of the parent but is not depended on by the parent to in order to
finish.</p>
<p>Since this is undertaken in a distributed environment a request to
another server, such as an RPC or a simple GET, can be considered a span
(a unit or work) for the local server. This causal link is what
OpenTracing aims to capture and visualise. In order to do this metadata
about the local server's span, i.e the 'span context', needs to be
included with the request to the remote.</p>
<p>It is up to the remote server to decide what it does with the spans it
creates. This is called the sampling policy and it can be configured
through Jaeger's settings.</p>
<p>For OpenTracing concepts see
<a href="https://opentracing.io/docs/overview/what-is-tracing/">https://opentracing.io/docs/overview/what-is-tracing/</a>.</p>
<p>For more information about Jaeger's implementation see
<a href="https://www.jaegertracing.io/docs/">https://www.jaegertracing.io/docs/</a></p>
<h2 id="setting-up-opentracing"><a class="header" href="#setting-up-opentracing">Setting up OpenTracing</a></h2>
<p>To receive OpenTracing spans, start up a Jaeger server. This can be done
using docker like so:</p>
<pre><code class="language-sh">docker run -d --name jaeger \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 14268:14268 \
jaegertracing/all-in-one:1
</code></pre>
<p>Latest documentation is probably at
https://www.jaegertracing.io/docs/latest/getting-started.</p>
<h2 id="enable-opentracing-in-synapse"><a class="header" href="#enable-opentracing-in-synapse">Enable OpenTracing in Synapse</a></h2>
<p>OpenTracing is not enabled by default. It must be enabled in the
homeserver config by uncommenting the config options under <code>opentracing</code>
as shown in the <a href="./sample_config.yaml">sample config</a>. For example:</p>
<pre><code class="language-yaml">opentracing:
enabled: true
homeserver_whitelist:
- "mytrustedhomeserver.org"
- "*.myotherhomeservers.com"
</code></pre>
<h2 id="homeserver-whitelisting"><a class="header" href="#homeserver-whitelisting">Homeserver whitelisting</a></h2>
<p>The homeserver whitelist is configured using regular expressions. A list
of regular expressions can be given and their union will be compared
when propagating any spans contexts to another homeserver.</p>
<p>Though it's mostly safe to send and receive span contexts to and from
untrusted users since span contexts are usually opaque ids it can lead
to two problems, namely:</p>
<ul>
<li>If the span context is marked as sampled by the sending homeserver
the receiver will sample it. Therefore two homeservers with wildly
different sampling policies could incur higher sampling counts than
intended.</li>
<li>Sending servers can attach arbitrary data to spans, known as
'baggage'. For safety this has been disabled in Synapse but that
doesn't prevent another server sending you baggage which will be
logged to OpenTracing's logs.</li>
</ul>
<h2 id="configuring-jaeger"><a class="header" href="#configuring-jaeger">Configuring Jaeger</a></h2>
<p>Sampling strategies can be set as in this document:
<a href="https://www.jaegertracing.io/docs/latest/sampling/">https://www.jaegertracing.io/docs/latest/sampling/</a>.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="synapse-database-schema-files"><a class="header" href="#synapse-database-schema-files">Synapse database schema files</a></h1>
<p>Synapse's database schema is stored in the <code>synapse.storage.schema</code> module.</p>
<h2 id="logical-databases"><a class="header" href="#logical-databases">Logical databases</a></h2>
<p>Synapse supports splitting its datastore across multiple physical databases (which can
be useful for large installations), and the schema files are therefore split according
to the logical database they apply to.</p>
<p>At the time of writing, the following "logical" databases are supported:</p>
<ul>
<li><code>state</code> - used to store Matrix room state (more specifically, <code>state_groups</code>,
their relationships and contents).</li>
<li><code>main</code> - stores everything else.</li>
</ul>
<p>Additionally, the <code>common</code> directory contains schema files for tables which must be
present on <em>all</em> physical databases.</p>
<h2 id="synapse-schema-versions"><a class="header" href="#synapse-schema-versions">Synapse schema versions</a></h2>
<p>Synapse manages its database schema via "schema versions". These are mainly used to
help avoid confusion if the Synapse codebase is rolled back after the database is
updated. They work as follows:</p>
<ul>
<li>
<p>The Synapse codebase defines a constant <code>synapse.storage.schema.SCHEMA_VERSION</code>
which represents the expectations made about the database by that version. For
example, as of Synapse v1.36, this is <code>59</code>.</p>
</li>
<li>
<p>The database stores a "compatibility version" in
<code>schema_compat_version.compat_version</code> which defines the <code>SCHEMA_VERSION</code> of the
oldest version of Synapse which will work with the database. On startup, if
<code>compat_version</code> is found to be newer than <code>SCHEMA_VERSION</code>, Synapse will refuse to
start.</p>
<p>Synapse automatically updates this field from
<code>synapse.storage.schema.SCHEMA_COMPAT_VERSION</code>.</p>
</li>
<li>
<p>Whenever a backwards-incompatible change is made to the database format (normally
via a <code>delta</code> file), <code>synapse.storage.schema.SCHEMA_COMPAT_VERSION</code> is also updated
so that administrators can not accidentally roll back to a too-old version of Synapse.</p>
</li>
</ul>
<p>Generally, the goal is to maintain compatibility with at least one or two previous
releases of Synapse, so any substantial change tends to require multiple releases and a
bit of forward-planning to get right.</p>
<p>As a worked example: we want to remove the <code>room_stats_historical</code> table. Here is how it
might pan out.</p>
<ol>
<li>
<p>Replace any code that <em>reads</em> from <code>room_stats_historical</code> with alternative
implementations, but keep writing to it in case of rollback to an earlier version.
Also, increase <code>synapse.storage.schema.SCHEMA_VERSION</code>. In this
instance, there is no existing code which reads from <code>room_stats_historical</code>, so
our starting point is:</p>
<p>v1.36.0: <code>SCHEMA_VERSION=59</code>, <code>SCHEMA_COMPAT_VERSION=59</code></p>
</li>
<li>
<p>Next (say in Synapse v1.37.0): remove the code that <em>writes</em> to
<code>room_stats_historical</code>, but don’t yet remove the table in case of rollback to
v1.36.0. Again, we increase <code>synapse.storage.schema.SCHEMA_VERSION</code>, but
because we have not broken compatibility with v1.36, we do not yet update
<code>SCHEMA_COMPAT_VERSION</code>. We now have:</p>
<p>v1.37.0: <code>SCHEMA_VERSION=60</code>, <code>SCHEMA_COMPAT_VERSION=59</code>.</p>
</li>
<li>
<p>Later (say in Synapse v1.38.0): we can remove the table altogether. This will
break compatibility with v1.36.0, so we must update <code>SCHEMA_COMPAT_VERSION</code> accordingly.
There is no need to update <code>synapse.storage.schema.SCHEMA_VERSION</code>, since there is no
change to the Synapse codebase here. So we end up with:</p>
<p>v1.38.0: <code>SCHEMA_VERSION=60</code>, <code>SCHEMA_COMPAT_VERSION=60</code>.</p>
</li>
</ol>
<p>If in doubt about whether to update <code>SCHEMA_VERSION</code> or not, it is generally best to
lean towards doing so.</p>
<h2 id="full-schema-dumps"><a class="header" href="#full-schema-dumps">Full schema dumps</a></h2>
<p>In the <code>full_schemas</code> directories, only the most recently-numbered snapshot is used
(<code>54</code> at the time of writing). Older snapshots (eg, <code>16</code>) are present for historical
reference only.</p>
<h3 id="building-full-schema-dumps"><a class="header" href="#building-full-schema-dumps">Building full schema dumps</a></h3>
<p>If you want to recreate these schemas, they need to be made from a database that
has had all background updates run.</p>
<p>To do so, use <code>scripts-dev/make_full_schema.sh</code>. This will produce new
<code>full.sql.postgres</code> and <code>full.sql.sqlite</code> files.</p>
<p>Ensure postgres is installed, then run:</p>
<pre><code>./scripts-dev/make_full_schema.sh -p postgres_username -o output_dir/
</code></pre>
<p>NB at the time of writing, this script predates the split into separate <code>state</code>/<code>main</code>
databases so will require updates to handle that correctly.</p>
<h2 id="boolean-columns"><a class="header" href="#boolean-columns">Boolean columns</a></h2>
<p>Boolean columns require special treatment, since SQLite treats booleans the
same as integers.</p>
<p>There are three separate aspects to this:</p>
<ul>
<li>
<p>Any new boolean column must be added to the <code>BOOLEAN_COLUMNS</code> list in
<code>scripts/synapse_port_db</code>. This tells the port script to cast the integer
value from SQLite to a boolean before writing the value to the postgres
database.</p>
</li>
<li>
<p>Before SQLite 3.23, <code>TRUE</code> and <code>FALSE</code> were not recognised as constants by
SQLite, and the <code>IS [NOT] TRUE</code>/<code>IS [NOT] FALSE</code> operators were not
supported. This makes it necessary to avoid using <code>TRUE</code> and <code>FALSE</code>
constants in SQL commands.</p>
<p>For example, to insert a <code>TRUE</code> value into the database, write:</p>
<pre><code class="language-python">txn.execute("INSERT INTO tbl(col) VALUES (?)", (True, ))
</code></pre>
</li>
<li>
<p>Default values for new boolean columns present a particular
difficulty. Generally it is best to create separate schema files for
Postgres and SQLite. For example:</p>
<pre><code class="language-sql"># in 00delta.sql.postgres:
ALTER TABLE tbl ADD COLUMN col BOOLEAN DEFAULT FALSE;
</code></pre>
<pre><code class="language-sql"># in 00delta.sql.sqlite:
ALTER TABLE tbl ADD COLUMN col BOOLEAN DEFAULT 0;
</code></pre>
<p>Note that there is a particularly insidious failure mode here: the Postgres
flavour will be accepted by SQLite 3.22, but will give a column whose
default value is the <strong>string</strong> <code>"FALSE"</code> - which, when cast back to a boolean
in Python, evaluates to <code>True</code>.</p>
</li>
</ul>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="log-contexts"><a class="header" href="#log-contexts">Log Contexts</a></h1>
<p>To help track the processing of individual requests, synapse uses a
'<code>log context</code>' to track which request it is handling at any given
moment. This is done via a thread-local variable; a <code>logging.Filter</code> is
then used to fish the information back out of the thread-local variable
and add it to each log record.</p>
<p>Logcontexts are also used for CPU and database accounting, so that we
can track which requests were responsible for high CPU use or database
activity.</p>
<p>The <code>synapse.logging.context</code> module provides a facilities for managing
the current log context (as well as providing the <code>LoggingContextFilter</code>
class).</p>
<p>Asynchronous functions make the whole thing complicated, so this document describes
how it all works, and how to write code which follows the rules.</p>
<p>In this document, "awaitable" refers to any object which can be <code>await</code>ed. In the context of
Synapse, that normally means either a coroutine or a Twisted
<a href="https://twistedmatrix.com/documents/current/api/twisted.internet.defer.Deferred.html"><code>Deferred</code></a>.</p>
<h2 id="logcontexts-without-asynchronous-code"><a class="header" href="#logcontexts-without-asynchronous-code">Logcontexts without asynchronous code</a></h2>
<p>In the absence of any asynchronous voodoo, things are simple enough. As with
any code of this nature, the rule is that our function should leave
things as it found them:</p>
<pre><code class="language-python">from synapse.logging import context # omitted from future snippets
def handle_request(request_id):
request_context = context.LoggingContext()
calling_context = context.set_current_context(request_context)
try:
request_context.request = request_id
do_request_handling()
logger.debug("finished")
finally:
context.set_current_context(calling_context)
def do_request_handling():
logger.debug("phew") # this will be logged against request_id
</code></pre>
<p>LoggingContext implements the context management methods, so the above
can be written much more succinctly as:</p>
<pre><code class="language-python">def handle_request(request_id):
with context.LoggingContext() as request_context:
request_context.request = request_id
do_request_handling()
logger.debug("finished")
def do_request_handling():
logger.debug("phew")
</code></pre>
<h2 id="using-logcontexts-with-awaitables"><a class="header" href="#using-logcontexts-with-awaitables">Using logcontexts with awaitables</a></h2>
<p>Awaitables break the linear flow of code so that there is no longer a single entry point
where we should set the logcontext and a single exit point where we should remove it.</p>
<p>Consider the example above, where <code>do_request_handling</code> needs to do some
blocking operation, and returns an awaitable:</p>
<pre><code class="language-python">async def handle_request(request_id):
with context.LoggingContext() as request_context:
request_context.request = request_id
await do_request_handling()
logger.debug("finished")
</code></pre>
<p>In the above flow:</p>
<ul>
<li>The logcontext is set</li>
<li><code>do_request_handling</code> is called, and returns an awaitable</li>
<li><code>handle_request</code> awaits the awaitable</li>
<li>Execution of <code>handle_request</code> is suspended</li>
</ul>
<p>So we have stopped processing the request (and will probably go on to
start processing the next), without clearing the logcontext.</p>
<p>To circumvent this problem, synapse code assumes that, wherever you have
an awaitable, you will want to <code>await</code> it. To that end, whereever
functions return awaitables, we adopt the following conventions:</p>
<p><strong>Rules for functions returning awaitables:</strong></p>
<blockquote>
<ul>
<li>If the awaitable is already complete, the function returns with the
same logcontext it started with.</li>
<li>If the awaitable is incomplete, the function clears the logcontext
before returning; when the awaitable completes, it restores the
logcontext before running any callbacks.</li>
</ul>
</blockquote>
<p>That sounds complicated, but actually it means a lot of code (including
the example above) "just works". There are two cases:</p>
<ul>
<li>
<p>If <code>do_request_handling</code> returns a completed awaitable, then the
logcontext will still be in place. In this case, execution will
continue immediately after the <code>await</code>; the "finished" line will
be logged against the right context, and the <code>with</code> block restores
the original context before we return to the caller.</p>
</li>
<li>
<p>If the returned awaitable is incomplete, <code>do_request_handling</code> clears
the logcontext before returning. The logcontext is therefore clear
when <code>handle_request</code> <code>await</code>s the awaitable.</p>
<p>Once <code>do_request_handling</code>'s awaitable completes, it will reinstate
the logcontext, before running the second half of <code>handle_request</code>,
so again the "finished" line will be logged against the right context,
and the <code>with</code> block restores the original context.</p>
</li>
</ul>
<p>As an aside, it's worth noting that <code>handle_request</code> follows our rules</p>
<ul>
<li>though that only matters if the caller has its own logcontext which it
cares about.</li>
</ul>
<p>The following sections describe pitfalls and helpful patterns when
implementing these rules.</p>
<h2 id="always-await-your-awaitables"><a class="header" href="#always-await-your-awaitables">Always await your awaitables</a></h2>
<p>Whenever you get an awaitable back from a function, you should <code>await</code> on
it as soon as possible. Do not pass go; do not do any logging; do not
call any other functions.</p>
<pre><code class="language-python">async def fun():
logger.debug("starting")
await do_some_stuff() # just like this
coro = more_stuff()
result = await coro # also fine, of course
return result
</code></pre>
<p>Provided this pattern is followed all the way back up to the callchain
to where the logcontext was set, this will make things work out ok:
provided <code>do_some_stuff</code> and <code>more_stuff</code> follow the rules above, then
so will <code>fun</code>.</p>
<p>It's all too easy to forget to <code>await</code>: for instance if we forgot that
<code>do_some_stuff</code> returned an awaitable, we might plough on regardless. This
leads to a mess; it will probably work itself out eventually, but not
before a load of stuff has been logged against the wrong context.
(Normally, other things will break, more obviously, if you forget to
<code>await</code>, so this tends not to be a major problem in practice.)</p>
<p>Of course sometimes you need to do something a bit fancier with your
awaitable - not all code follows the linear A-then-B-then-C pattern.
Notes on implementing more complex patterns are in later sections.</p>
<h2 id="where-you-create-a-new-awaitable-make-it-follow-the-rules"><a class="header" href="#where-you-create-a-new-awaitable-make-it-follow-the-rules">Where you create a new awaitable, make it follow the rules</a></h2>
<p>Most of the time, an awaitable comes from another synapse function.
Sometimes, though, we need to make up a new awaitable, or we get an awaitable
back from external code. We need to make it follow our rules.</p>
<p>The easy way to do it is by using <code>context.make_deferred_yieldable</code>. Suppose we want to implement
<code>sleep</code>, which returns a deferred which will run its callbacks after a
given number of seconds. That might look like:</p>
<pre><code class="language-python"># not a logcontext-rules-compliant function
def get_sleep_deferred(seconds):
d = defer.Deferred()
reactor.callLater(seconds, d.callback, None)
return d
</code></pre>
<p>That doesn't follow the rules, but we can fix it by calling it through
<code>context.make_deferred_yieldable</code>:</p>
<pre><code class="language-python">async def sleep(seconds):
return await context.make_deferred_yieldable(get_sleep_deferred(seconds))
</code></pre>
<h2 id="fire-and-forget"><a class="header" href="#fire-and-forget">Fire-and-forget</a></h2>
<p>Sometimes you want to fire off a chain of execution, but not wait for
its result. That might look a bit like this:</p>
<pre><code class="language-python">async def do_request_handling():
await foreground_operation()
# *don't* do this
background_operation()
logger.debug("Request handling complete")
async def background_operation():
await first_background_step()
logger.debug("Completed first step")
await second_background_step()
logger.debug("Completed second step")
</code></pre>
<p>The above code does a couple of steps in the background after
<code>do_request_handling</code> has finished. The log lines are still logged
against the <code>request_context</code> logcontext, which may or may not be
desirable. There are two big problems with the above, however. The first
problem is that, if <code>background_operation</code> returns an incomplete
awaitable, it will expect its caller to <code>await</code> immediately, so will have
cleared the logcontext. In this example, that means that 'Request
handling complete' will be logged without any context.</p>
<p>The second problem, which is potentially even worse, is that when the
awaitable returned by <code>background_operation</code> completes, it will restore
the original logcontext. There is nothing waiting on that awaitable, so
the logcontext will leak into the reactor and possibly get attached to
some arbitrary future operation.</p>
<p>There are two potential solutions to this.</p>
<p>One option is to surround the call to <code>background_operation</code> with a
<code>PreserveLoggingContext</code> call. That will reset the logcontext before
starting <code>background_operation</code> (so the context restored when the
deferred completes will be the empty logcontext), and will restore the
current logcontext before continuing the foreground process:</p>
<pre><code class="language-python">async def do_request_handling():
await foreground_operation()
# start background_operation off in the empty logcontext, to
# avoid leaking the current context into the reactor.
with PreserveLoggingContext():
background_operation()
# this will now be logged against the request context
logger.debug("Request handling complete")
</code></pre>
<p>Obviously that option means that the operations done in
<code>background_operation</code> would be not be logged against a logcontext
(though that might be fixed by setting a different logcontext via a
<code>with LoggingContext(...)</code> in <code>background_operation</code>).</p>
<p>The second option is to use <code>context.run_in_background</code>, which wraps a
function so that it doesn't reset the logcontext even when it returns
an incomplete awaitable, and adds a callback to the returned awaitable to
reset the logcontext. In other words, it turns a function that follows
the Synapse rules about logcontexts and awaitables into one which behaves
more like an external function --- the opposite operation to that
described in the previous section. It can be used like this:</p>
<pre><code class="language-python">async def do_request_handling():
await foreground_operation()
context.run_in_background(background_operation)
# this will now be logged against the request context
logger.debug("Request handling complete")
</code></pre>
<h2 id="passing-synapse-deferreds-into-third-party-functions"><a class="header" href="#passing-synapse-deferreds-into-third-party-functions">Passing synapse deferreds into third-party functions</a></h2>
<p>A typical example of this is where we want to collect together two or
more awaitables via <code>defer.gatherResults</code>:</p>
<pre><code class="language-python">a1 = operation1()
a2 = operation2()
a3 = defer.gatherResults([a1, a2])
</code></pre>
<p>This is really a variation of the fire-and-forget problem above, in that
we are firing off <code>a1</code> and <code>a2</code> without awaiting on them. The difference
is that we now have third-party code attached to their callbacks. Anyway
either technique given in the <a href="log_contexts.html#fire-and-forget">Fire-and-forget</a>
section will work.</p>
<p>Of course, the new awaitable returned by <code>gather</code> needs to be
wrapped in order to make it follow the logcontext rules before we can
yield it, as described in <a href="log_contexts.html#where-you-create-a-new-awaitable-make-it-follow-the-rules">Where you create a new awaitable, make it
follow the
rules</a>.</p>
<p>So, option one: reset the logcontext before starting the operations to
be gathered:</p>
<pre><code class="language-python">async def do_request_handling():
with PreserveLoggingContext():
a1 = operation1()
a2 = operation2()
result = await defer.gatherResults([a1, a2])
</code></pre>
<p>In this case particularly, though, option two, of using
<code>context.run_in_background</code> almost certainly makes more sense, so that
<code>operation1</code> and <code>operation2</code> are both logged against the original
logcontext. This looks like:</p>
<pre><code class="language-python">async def do_request_handling():
a1 = context.run_in_background(operation1)
a2 = context.run_in_background(operation2)
result = await make_deferred_yieldable(defer.gatherResults([a1, a2]))
</code></pre>
<h2 id="a-note-on-garbage-collection-of-awaitable-chains"><a class="header" href="#a-note-on-garbage-collection-of-awaitable-chains">A note on garbage-collection of awaitable chains</a></h2>
<p>It turns out that our logcontext rules do not play nicely with awaitable
chains which get orphaned and garbage-collected.</p>
<p>Imagine we have some code that looks like this:</p>
<pre><code class="language-python">listener_queue = []
def on_something_interesting():
for d in listener_queue:
d.callback("foo")
async def await_something_interesting():
new_awaitable = defer.Deferred()
listener_queue.append(new_awaitable)
with PreserveLoggingContext():
await new_awaitable
</code></pre>
<p>Obviously, the idea here is that we have a bunch of things which are
waiting for an event. (It's just an example of the problem here, but a
relatively common one.)</p>
<p>Now let's imagine two further things happen. First of all, whatever was
waiting for the interesting thing goes away. (Perhaps the request times
out, or something <em>even more</em> interesting happens.)</p>
<p>Secondly, let's suppose that we decide that the interesting thing is
never going to happen, and we reset the listener queue:</p>
<pre><code class="language-python">def reset_listener_queue():
listener_queue.clear()
</code></pre>
<p>So, both ends of the awaitable chain have now dropped their references,
and the awaitable chain is now orphaned, and will be garbage-collected at
some point. Note that <code>await_something_interesting</code> is a coroutine,
which Python implements as a generator function. When Python
garbage-collects generator functions, it gives them a chance to
clean up by making the <code>async</code> (or <code>yield</code>) raise a <code>GeneratorExit</code>
exception. In our case, that means that the <code>__exit__</code> handler of
<code>PreserveLoggingContext</code> will carefully restore the request context, but
there is now nothing waiting for its return, so the request context is
never cleared.</p>
<p>To reiterate, this problem only arises when <em>both</em> ends of a awaitable
chain are dropped. Dropping the the reference to an awaitable you're
supposed to be awaiting is bad practice, so this doesn't
actually happen too much. Unfortunately, when it does happen, it will
lead to leaked logcontexts which are incredibly hard to track down.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="replication-architecture"><a class="header" href="#replication-architecture">Replication Architecture</a></h1>
<h2 id="motivation"><a class="header" href="#motivation">Motivation</a></h2>
<p>We'd like to be able to split some of the work that synapse does into
multiple python processes. In theory multiple synapse processes could
share a single postgresql database and we'd scale up by running more
synapse processes. However much of synapse assumes that only one process
is interacting with the database, both for assigning unique identifiers
when inserting into tables, notifying components about new updates, and
for invalidating its caches.</p>
<p>So running multiple copies of the current code isn't an option. One way
to run multiple processes would be to have a single writer process and
multiple reader processes connected to the same database. In order to do
this we'd need a way for the reader process to invalidate its in-memory
caches when an update happens on the writer. One way to do this is for
the writer to present an append-only log of updates which the readers
can consume to invalidate their caches and to push updates to listening
clients or pushers.</p>
<p>Synapse already stores much of its data as an append-only log so that it
can correctly respond to <code>/sync</code> requests so the amount of code changes
needed to expose the append-only log to the readers should be fairly
minimal.</p>
<h2 id="architecture"><a class="header" href="#architecture">Architecture</a></h2>
<h3 id="the-replication-protocol"><a class="header" href="#the-replication-protocol">The Replication Protocol</a></h3>
<p>See <a href="tcp_replication.html">the TCP replication documentation</a>.</p>
<h3 id="the-slaved-datastore"><a class="header" href="#the-slaved-datastore">The Slaved DataStore</a></h3>
<p>There are read-only version of the synapse storage layer in
<code>synapse/replication/slave/storage</code> that use the response of the
replication API to invalidate their caches.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="tcp-replication"><a class="header" href="#tcp-replication">TCP Replication</a></h1>
<h2 id="motivation-1"><a class="header" href="#motivation-1">Motivation</a></h2>
<p>Previously the workers used an HTTP long poll mechanism to get updates
from the master, which had the problem of causing a lot of duplicate
work on the server. This TCP protocol replaces those APIs with the aim
of increased efficiency.</p>
<h2 id="overview-3"><a class="header" href="#overview-3">Overview</a></h2>
<p>The protocol is based on fire and forget, line based commands. An
example flow would be (where '>' indicates master to worker and
'<' worker to master flows):</p>
<pre><code>> SERVER example.com
< REPLICATE
> POSITION events master 53 53
> RDATA events master 54 ["$foo1:bar.com", ...]
> RDATA events master 55 ["$foo4:bar.com", ...]
</code></pre>
<p>The example shows the server accepting a new connection and sending its identity
with the <code>SERVER</code> command, followed by the client server to respond with the
position of all streams. The server then periodically sends <code>RDATA</code> commands
which have the format <code>RDATA <stream_name> <instance_name> <token> <row></code>, where
the format of <code><row></code> is defined by the individual streams. The
<code><instance_name></code> is the name of the Synapse process that generated the data
(usually "master").</p>
<p>Error reporting happens by either the client or server sending an ERROR
command, and usually the connection will be closed.</p>
<p>Since the protocol is a simple line based, its possible to manually
connect to the server using a tool like netcat. A few things should be
noted when manually using the protocol:</p>
<ul>
<li>The federation stream is only available if federation sending has
been disabled on the main process.</li>
<li>The server will only time connections out that have sent a <code>PING</code>
command. If a ping is sent then the connection will be closed if no
further commands are receieved within 15s. Both the client and
server protocol implementations will send an initial PING on
connection and ensure at least one command every 5s is sent (not
necessarily <code>PING</code>).</li>
<li><code>RDATA</code> commands <em>usually</em> include a numeric token, however if the
stream has multiple rows to replicate per token the server will send
multiple <code>RDATA</code> commands, with all but the last having a token of
<code>batch</code>. See the documentation on <code>commands.RdataCommand</code> for
further details.</li>
</ul>
<h2 id="architecture-1"><a class="header" href="#architecture-1">Architecture</a></h2>
<p>The basic structure of the protocol is line based, where the initial
word of each line specifies the command. The rest of the line is parsed
based on the command. For example, the RDATA command is defined as:</p>
<pre><code>RDATA <stream_name> <instance_name> <token> <row_json>
</code></pre>
<p>(Note that <row_json> may contains spaces, but cannot contain
newlines.)</p>
<p>Blank lines are ignored.</p>
<h3 id="keep-alives"><a class="header" href="#keep-alives">Keep alives</a></h3>
<p>Both sides are expected to send at least one command every 5s or so, and
should send a <code>PING</code> command if necessary. If either side do not receive
a command within e.g. 15s then the connection should be closed.</p>
<p>Because the server may be connected to manually using e.g. netcat, the
timeouts aren't enabled until an initial <code>PING</code> command is seen. Both
the client and server implementations below send a <code>PING</code> command
immediately on connection to ensure the timeouts are enabled.</p>
<p>This ensures that both sides can quickly realize if the tcp connection
has gone and handle the situation appropriately.</p>
<h3 id="start-up"><a class="header" href="#start-up">Start up</a></h3>
<p>When a new connection is made, the server:</p>
<ul>
<li>Sends a <code>SERVER</code> command, which includes the identity of the server,
allowing the client to detect if its connected to the expected
server</li>
<li>Sends a <code>PING</code> command as above, to enable the client to time out
connections promptly.</li>
</ul>
<p>The client:</p>
<ul>
<li>Sends a <code>NAME</code> command, allowing the server to associate a human
friendly name with the connection. This is optional.</li>
<li>Sends a <code>PING</code> as above</li>
<li>Sends a <code>REPLICATE</code> to get the current position of all streams.</li>
<li>On receipt of a <code>SERVER</code> command, checks that the server name
matches the expected server name.</li>
</ul>
<h3 id="error-handling"><a class="header" href="#error-handling">Error handling</a></h3>
<p>If either side detects an error it can send an <code>ERROR</code> command and close
the connection.</p>
<p>If the client side loses the connection to the server it should
reconnect, following the steps above.</p>
<h3 id="congestion"><a class="header" href="#congestion">Congestion</a></h3>
<p>If the server sends messages faster than the client can consume them the
server will first buffer a (fairly large) number of commands and then
disconnect the client. This ensures that we don't queue up an unbounded
number of commands in memory and gives us a potential oppurtunity to
squawk loudly. When/if the client recovers it can reconnect to the
server and ask for missed messages.</p>
<h3 id="reliability"><a class="header" href="#reliability">Reliability</a></h3>
<p>In general the replication stream should be considered an unreliable
transport since e.g. commands are not resent if the connection
disappears.</p>
<p>The exception to that are the replication streams, i.e. RDATA commands,
since these include tokens which can be used to restart the stream on
connection errors.</p>
<p>The client should keep track of the token in the last RDATA command
received for each stream so that on reconneciton it can start streaming
from the correct place. Note: not all RDATA have valid tokens due to
batching. See <code>RdataCommand</code> for more details.</p>
<h3 id="example-5"><a class="header" href="#example-5">Example</a></h3>
<p>An example iteraction is shown below. Each line is prefixed with '>'
or '<' to indicate which side is sending, these are <em>not</em> included on
the wire:</p>
<pre><code>* connection established *
> SERVER localhost:8823
> PING 1490197665618
< NAME synapse.app.appservice
< PING 1490197665618
< REPLICATE
> POSITION events master 1 1
> POSITION backfill master 1 1
> POSITION caches master 1 1
> RDATA caches master 2 ["get_user_by_id",["@01register-user:localhost:8823"],1490197670513]
> RDATA events master 14 ["$149019767112vOHxz:localhost:8823",
"!AFDCvgApUmpdfVjIXm:localhost:8823","m.room.guest_access","",null]
< PING 1490197675618
> ERROR server stopping
* connection closed by server *
</code></pre>
<p>The <code>POSITION</code> command sent by the server is used to set the clients
position without needing to send data with the <code>RDATA</code> command.</p>
<p>An example of a batched set of <code>RDATA</code> is:</p>
<pre><code>> RDATA caches master batch ["get_user_by_id",["@test:localhost:8823"],1490197670513]
> RDATA caches master batch ["get_user_by_id",["@test2:localhost:8823"],1490197670513]
> RDATA caches master batch ["get_user_by_id",["@test3:localhost:8823"],1490197670513]
> RDATA caches master 54 ["get_user_by_id",["@test4:localhost:8823"],1490197670513]
</code></pre>
<p>In this case the client shouldn't advance their caches token until it
sees the the last <code>RDATA</code>.</p>
<h3 id="list-of-commands"><a class="header" href="#list-of-commands">List of commands</a></h3>
<p>The list of valid commands, with which side can send it: server (S) or
client (C):</p>
<h4 id="server-s"><a class="header" href="#server-s">SERVER (S)</a></h4>
<p>Sent at the start to identify which server the client is talking to</p>
<h4 id="rdata-s"><a class="header" href="#rdata-s">RDATA (S)</a></h4>
<p>A single update in a stream</p>
<h4 id="position-s"><a class="header" href="#position-s">POSITION (S)</a></h4>
<p>On receipt of a POSITION command clients should check if they have missed any
updates, and if so then fetch them out of band. Sent in response to a
REPLICATE command (but can happen at any time).</p>
<p>The POSITION command includes the source of the stream. Currently all streams
are written by a single process (usually "master"). If fetching missing
updates via HTTP API, rather than via the DB, then processes should make the
request to the appropriate process.</p>
<p>Two positions are included, the "new" position and the last position sent respectively.
This allows servers to tell instances that the positions have advanced but no
data has been written, without clients needlessly checking to see if they
have missed any updates.</p>
<h4 id="error-s-c"><a class="header" href="#error-s-c">ERROR (S, C)</a></h4>
<p>There was an error</p>
<h4 id="ping-s-c"><a class="header" href="#ping-s-c">PING (S, C)</a></h4>
<p>Sent periodically to ensure the connection is still alive</p>
<h4 id="name-c"><a class="header" href="#name-c">NAME (C)</a></h4>
<p>Sent at the start by client to inform the server who they are</p>
<h4 id="replicate-c"><a class="header" href="#replicate-c">REPLICATE (C)</a></h4>
<p>Asks the server for the current position of all streams.</p>
<h4 id="user_sync-c"><a class="header" href="#user_sync-c">USER_SYNC (C)</a></h4>
<p>A user has started or stopped syncing on this process.</p>
<h4 id="clear_user_sync-c"><a class="header" href="#clear_user_sync-c">CLEAR_USER_SYNC (C)</a></h4>
<p>The server should clear all associated user sync data from the worker.</p>
<p>This is used when a worker is shutting down.</p>
<h4 id="federation_ack-c"><a class="header" href="#federation_ack-c">FEDERATION_ACK (C)</a></h4>
<p>Acknowledge receipt of some federation data</p>
<h3 id="remote_server_up-s-c"><a class="header" href="#remote_server_up-s-c">REMOTE_SERVER_UP (S, C)</a></h3>
<p>Inform other processes that a remote server may have come back online.</p>
<p>See <code>synapse/replication/tcp/commands.py</code> for a detailed description and
the format of each command.</p>
<h3 id="cache-invalidation-stream"><a class="header" href="#cache-invalidation-stream">Cache Invalidation Stream</a></h3>
<p>The cache invalidation stream is used to inform workers when they need
to invalidate any of their caches in the data store. This is done by
streaming all cache invalidations done on master down to the workers,
assuming that any caches on the workers also exist on the master.</p>
<p>Each individual cache invalidation results in a row being sent down
replication, which includes the cache name (the name of the function)
and they key to invalidate. For example:</p>
<pre><code>> RDATA caches master 550953771 ["get_user_by_id", ["@bob:example.com"], 1550574873251]
</code></pre>
<p>Alternatively, an entire cache can be invalidated by sending down a <code>null</code>
instead of the key. For example:</p>
<pre><code>> RDATA caches master 550953772 ["get_user_by_id", null, 1550574873252]
</code></pre>
<p>However, there are times when a number of caches need to be invalidated
at the same time with the same key. To reduce traffic we batch those
invalidations into a single poke by defining a special cache name that
workers understand to mean to expand to invalidate the correct caches.</p>
<p>Currently the special cache names are declared in
<code>synapse/storage/_base.py</code> and are:</p>
<ol>
<li><code>cs_cache_fake</code> ─ invalidates caches that depend on the current
state</li>
</ol>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="internal-documentation"><a class="header" href="#internal-documentation">Internal Documentation</a></h1>
<p>This section covers implementation documentation for various parts of Synapse.</p>
<p>If a developer is planning to make a change to a feature of Synapse, it can be useful for
general documentation of how that feature is implemented to be available. This saves the
developer time in place of needing to understand how the feature works by reading the
code.</p>
<p>Documentation that would be more useful for the perspective of a system administrator,
rather than a developer who's intending to change to code, should instead be placed
under the Usage section of the documentation.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="how-to-test-saml-as-a-developer-without-a-server"><a class="header" href="#how-to-test-saml-as-a-developer-without-a-server">How to test SAML as a developer without a server</a></h1>
<p>https://capriza.github.io/samling/samling.html (https://github.com/capriza/samling) is a great
resource for being able to tinker with the SAML options within Synapse without needing to
deploy and configure a complicated software stack.</p>
<p>To make Synapse (and therefore Riot) use it:</p>
<ol>
<li>Use the samling.html URL above or deploy your own and visit the IdP Metadata tab.</li>
<li>Copy the XML to your clipboard.</li>
<li>On your Synapse server, create a new file <code>samling.xml</code> next to your <code>homeserver.yaml</code> with
the XML from step 2 as the contents.</li>
<li>Edit your <code>homeserver.yaml</code> to include:
<pre><code class="language-yaml">saml2_config:
sp_config:
allow_unknown_attributes: true # Works around a bug with AVA Hashes: https://github.com/IdentityPython/pysaml2/issues/388
metadata:
local: ["samling.xml"]
</code></pre>
</li>
<li>Ensure that your <code>homeserver.yaml</code> has a setting for <code>public_baseurl</code>:
<pre><code class="language-yaml">public_baseurl: http://localhost:8080/
</code></pre>
</li>
<li>Run <code>apt-get install xmlsec1</code> and <code>pip install --upgrade --force 'pysaml2>=4.5.0'</code> to ensure
the dependencies are installed and ready to go.</li>
<li>Restart Synapse.</li>
</ol>
<p>Then in Riot:</p>
<ol>
<li>Visit the login page with a Riot pointing at your homeserver.</li>
<li>Click the Single Sign-On button.</li>
<li>On the samling page, enter a Name Identifier and add a SAML Attribute for <code>uid=your_localpart</code>.
The response must also be signed.</li>
<li>Click "Next".</li>
<li>Click "Post Response" (change nothing).</li>
<li>You should be logged in.</li>
</ol>
<p>If you try and repeat this process, you may be automatically logged in using the information you
gave previously. To fix this, open your developer console (<code>F12</code> or <code>Ctrl+Shift+I</code>) while on the
samling page and clear the site data. In Chrome, this will be a button on the Application tab.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="how-to-test-cas-as-a-developer-without-a-server"><a class="header" href="#how-to-test-cas-as-a-developer-without-a-server">How to test CAS as a developer without a server</a></h1>
<p>The <a href="https://github.com/jbittel/django-mama-cas">django-mama-cas</a> project is an
easy to run CAS implementation built on top of Django.</p>
<h2 id="prerequisites"><a class="header" href="#prerequisites">Prerequisites</a></h2>
<ol>
<li>Create a new virtualenv: <code>python3 -m venv <your virtualenv></code></li>
<li>Activate your virtualenv: <code>source /path/to/your/virtualenv/bin/activate</code></li>
<li>Install Django and django-mama-cas:
<pre><code>python -m pip install "django<3" "django-mama-cas==2.4.0"
</code></pre>
</li>
<li>Create a Django project in the current directory:
<pre><code>django-admin startproject cas_test .
</code></pre>
</li>
<li>Follow the <a href="https://django-mama-cas.readthedocs.io/en/latest/installation.html#configuring">install directions</a> for django-mama-cas</li>
<li>Setup the SQLite database: <code>python manage.py migrate</code></li>
<li>Create a user:
<pre><code>python manage.py createsuperuser
</code></pre>
<ol>
<li>Use whatever you want as the username and password.</li>
<li>Leave the other fields blank.</li>
</ol>
</li>
<li>Use the built-in Django test server to serve the CAS endpoints on port 8000:
<pre><code>python manage.py runserver
</code></pre>
</li>
</ol>
<p>You should now have a Django project configured to serve CAS authentication with
a single user created.</p>
<h2 id="configure-synapse-and-element-to-use-cas"><a class="header" href="#configure-synapse-and-element-to-use-cas">Configure Synapse (and Element) to use CAS</a></h2>
<ol>
<li>Modify your <code>homeserver.yaml</code> to enable CAS and point it to your locally
running Django test server:
<pre><code class="language-yaml">cas_config:
enabled: true
server_url: "http://localhost:8000"
service_url: "http://localhost:8081"
#displayname_attribute: name
#required_attributes:
# name: value
</code></pre>
</li>
<li>Restart Synapse.</li>
</ol>
<p>Note that the above configuration assumes the homeserver is running on port 8081
and that the CAS server is on port 8000, both on localhost.</p>
<h2 id="testing-the-configuration"><a class="header" href="#testing-the-configuration">Testing the configuration</a></h2>
<p>Then in Element:</p>
<ol>
<li>Visit the login page with a Element pointing at your homeserver.</li>
<li>Click the Single Sign-On button.</li>
<li>Login using the credentials created with <code>createsuperuser</code>.</li>
<li>You should be logged in.</li>
</ol>
<p>If you want to repeat this process you'll need to manually logout first:</p>
<ol>
<li>http://localhost:8000/admin/</li>
<li>Click "logout" in the top right.</li>
</ol>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="auth-chain-difference-algorithm"><a class="header" href="#auth-chain-difference-algorithm">Auth Chain Difference Algorithm</a></h1>
<p>The auth chain difference algorithm is used by V2 state resolution, where a
naive implementation can be a significant source of CPU and DB usage.</p>
<h3 id="definitions"><a class="header" href="#definitions">Definitions</a></h3>
<p>A <em>state set</em> is a set of state events; e.g. the input of a state resolution
algorithm is a collection of state sets.</p>
<p>The <em>auth chain</em> of a set of events are all the events' auth events and <em>their</em>
auth events, recursively (i.e. the events reachable by walking the graph induced
by an event's auth events links).</p>
<p>The <em>auth chain difference</em> of a collection of state sets is the union minus the
intersection of the sets of auth chains corresponding to the state sets, i.e an
event is in the auth chain difference if it is reachable by walking the auth
event graph from at least one of the state sets but not from <em>all</em> of the state
sets.</p>
<h2 id="breadth-first-walk-algorithm"><a class="header" href="#breadth-first-walk-algorithm">Breadth First Walk Algorithm</a></h2>
<p>A way of calculating the auth chain difference without calculating the full auth
chains for each state set is to do a parallel breadth first walk (ordered by
depth) of each state set's auth chain. By tracking which events are reachable
from each state set we can finish early if every pending event is reachable from
every state set.</p>
<p>This can work well for state sets that have a small auth chain difference, but
can be very inefficient for larger differences. However, this algorithm is still
used if we don't have a chain cover index for the room (e.g. because we're in
the process of indexing it).</p>
<h2 id="chain-cover-index"><a class="header" href="#chain-cover-index">Chain Cover Index</a></h2>
<p>Synapse computes auth chain differences by pre-computing a "chain cover" index
for the auth chain in a room, allowing efficient reachability queries like "is
event A in the auth chain of event B". This is done by assigning every event a
<em>chain ID</em> and <em>sequence number</em> (e.g. <code>(5,3)</code>), and having a map of <em>links</em>
between chains (e.g. <code>(5,3) -> (2,4)</code>) such that A is reachable by B (i.e. <code>A</code>
is in the auth chain of <code>B</code>) if and only if either:</p>
<ol>
<li>A and B have the same chain ID and <code>A</code>'s sequence number is less than <code>B</code>'s
sequence number; or</li>
<li>there is a link <code>L</code> between <code>B</code>'s chain ID and <code>A</code>'s chain ID such that
<code>L.start_seq_no</code> <= <code>B.seq_no</code> and <code>A.seq_no</code> <= <code>L.end_seq_no</code>.</li>
</ol>
<p>There are actually two potential implementations, one where we store links from
each chain to every other reachable chain (the transitive closure of the links
graph), and one where we remove redundant links (the transitive reduction of the
links graph) e.g. if we have chains <code>C3 -> C2 -> C1</code> then the link <code>C3 -> C1</code>
would not be stored. Synapse uses the former implementations so that it doesn't
need to recurse to test reachability between chains.</p>
<h3 id="example-6"><a class="header" href="#example-6">Example</a></h3>
<p>An example auth graph would look like the following, where chains have been
formed based on type/state_key and are denoted by colour and are labelled with
<code>(chain ID, sequence number)</code>. Links are denoted by the arrows (links in grey
are those that would be remove in the second implementation described above).</p>
<p><img src="auth_chain_diff.dot.png" alt="Example" /></p>
<p>Note that we don't include all links between events and their auth events, as
most of those links would be redundant. For example, all events point to the
create event, but each chain only needs the one link from it's base to the
create event.</p>
<h2 id="using-the-index"><a class="header" href="#using-the-index">Using the Index</a></h2>
<p>This index can be used to calculate the auth chain difference of the state sets
by looking at the chain ID and sequence numbers reachable from each state set:</p>
<ol>
<li>For every state set lookup the chain ID/sequence numbers of each state event</li>
<li>Use the index to find all chains and the maximum sequence number reachable
from each state set.</li>
<li>The auth chain difference is then all events in each chain that have sequence
numbers between the maximum sequence number reachable from <em>any</em> state set and
the minimum reachable by <em>all</em> state sets (if any).</li>
</ol>
<p>Note that steps 2 is effectively calculating the auth chain for each state set
(in terms of chain IDs and sequence numbers), and step 3 is calculating the
difference between the union and intersection of the auth chains.</p>
<h3 id="worked-example"><a class="header" href="#worked-example">Worked Example</a></h3>
<p>For example, given the above graph, we can calculate the difference between
state sets consisting of:</p>
<ol>
<li><code>S1</code>: Alice's invite <code>(4,1)</code> and Bob's second join <code>(2,2)</code>; and</li>
<li><code>S2</code>: Alice's second join <code>(4,3)</code> and Bob's first join <code>(2,1)</code>.</li>
</ol>
<p>Using the index we see that the following auth chains are reachable from each
state set:</p>
<ol>
<li><code>S1</code>: <code>(1,1)</code>, <code>(2,2)</code>, <code>(3,1)</code> & <code>(4,1)</code></li>
<li><code>S2</code>: <code>(1,1)</code>, <code>(2,1)</code>, <code>(3,2)</code> & <code>(4,3)</code></li>
</ol>
<p>And so, for each the ranges that are in the auth chain difference:</p>
<ol>
<li>Chain 1: None, (since everything can reach the create event).</li>
<li>Chain 2: The range <code>(1, 2]</code> (i.e. just <code>2</code>), as <code>1</code> is reachable by all state
sets and the maximum reachable is <code>2</code> (corresponding to Bob's second join).</li>
<li>Chain 3: Similarly the range <code>(1, 2]</code> (corresponding to the second power
level).</li>
<li>Chain 4: The range <code>(1, 3]</code> (corresponding to both of Alice's joins).</li>
</ol>
<p>So the final result is: Bob's second join <code>(2,2)</code>, the second power level
<code>(3,2)</code> and both of Alice's joins <code>(4,2)</code> & <code>(4,3)</code>.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="media-repository"><a class="header" href="#media-repository">Media Repository</a></h1>
<p><em>Synapse implementation-specific details for the media repository</em></p>
<p>The media repository is where attachments and avatar photos are stored.
It stores attachment content and thumbnails for media uploaded by local users.
It caches attachment content and thumbnails for media uploaded by remote users.</p>
<h2 id="storage"><a class="header" href="#storage">Storage</a></h2>
<p>Each item of media is assigned a <code>media_id</code> when it is uploaded.
The <code>media_id</code> is a randomly chosen, URL safe 24 character string.</p>
<p>Metadata such as the MIME type, upload time and length are stored in the
sqlite3 database indexed by <code>media_id</code>.</p>
<p>Content is stored on the filesystem under a <code>"local_content"</code> directory.</p>
<p>Thumbnails are stored under a <code>"local_thumbnails"</code> directory.</p>
<p>The item with <code>media_id</code> <code>"aabbccccccccdddddddddddd"</code> is stored under
<code>"local_content/aa/bb/ccccccccdddddddddddd"</code>. Its thumbnail with width
<code>128</code> and height <code>96</code> and type <code>"image/jpeg"</code> is stored under
<code>"local_thumbnails/aa/bb/ccccccccdddddddddddd/128-96-image-jpeg"</code></p>
<p>Remote content is cached under <code>"remote_content"</code> directory. Each item of
remote content is assigned a local <code>"filesystem_id"</code> to ensure that the
directory structure <code>"remote_content/server_name/aa/bb/ccccccccdddddddddddd"</code>
is appropriate. Thumbnails for remote content are stored under
<code>"remote_thumbnails/server_name/..."</code></p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="room-and-user-statistics"><a class="header" href="#room-and-user-statistics">Room and User Statistics</a></h1>
<p>Synapse maintains room and user statistics in various tables. These can be used
for administrative purposes but are also used when generating the public room
directory.</p>
<h1 id="synapse-developer-documentation"><a class="header" href="#synapse-developer-documentation">Synapse Developer Documentation</a></h1>
<h2 id="high-level-concepts"><a class="header" href="#high-level-concepts">High-Level Concepts</a></h2>
<h3 id="definitions-1"><a class="header" href="#definitions-1">Definitions</a></h3>
<ul>
<li><strong>subject</strong>: Something we are tracking stats about – currently a room or user.</li>
<li><strong>current row</strong>: An entry for a subject in the appropriate current statistics
table. Each subject can have only one.</li>
</ul>
<h3 id="overview-4"><a class="header" href="#overview-4">Overview</a></h3>
<p>Stats correspond to the present values. Current rows contain the most up-to-date
statistics for a room. Each subject can only have one entry.</p>
<div id="chapter_begin" style="break-before: page; page-break-before: always;"></div><h1 id="deprecation-policy-for-platform-dependencies"><a class="header" href="#deprecation-policy-for-platform-dependencies">Deprecation Policy for Platform Dependencies</a></h1>
<p>Synapse has a number of platform dependencies, including Python and PostgreSQL.
This document outlines the policy towards which versions we support, and when we
drop support for versions in the future.</p>
<h2 id="policy"><a class="header" href="#policy">Policy</a></h2>
<p>Synapse follows the upstream support life cycles for Python and PostgreSQL,
i.e. when a version reaches End of Life Synapse will withdraw support for that
version in future releases.</p>
<p>Details on the upstream support life cycles for Python and PostgreSQL are
documented at https://endoflife.date/python and
https://endoflife.date/postgresql.</p>
<h2 id="context"><a class="header" href="#context">Context</a></h2>
<p>It is important for system admins to have a clear understanding of the platform
requirements of Synapse and its deprecation policies so that they can
effectively plan upgrading their infrastructure ahead of time. This is
especially important in contexts where upgrading the infrastructure requires
auditing and approval from a security team, or where otherwise upgrading is a
long process.</p>
<p>By following the upstream support life cycles Synapse can ensure that its
dependencies continue to get security patches, while not requiring system admins
to constantly update their platform dependencies to the latest versions.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
</nav>
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
<script type="text/javascript" src="docs/website_files/table-of-contents.js"></script>
<script type="text/javascript">
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
</body>
</html>
|