1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.dendrite;
9 settingsFormat = pkgs.formats.yaml { };
10 configurationYaml = settingsFormat.generate "dendrite.yaml" cfg.settings;
11 workingDir = "/var/lib/dendrite";
12in
13{
14 options.services.dendrite = {
15 enable = lib.mkEnableOption "matrix.org dendrite";
16 httpPort = lib.mkOption {
17 type = lib.types.nullOr lib.types.port;
18 default = 8008;
19 description = ''
20 The port to listen for HTTP requests on.
21 '';
22 };
23 httpsPort = lib.mkOption {
24 type = lib.types.nullOr lib.types.port;
25 default = null;
26 description = ''
27 The port to listen for HTTPS requests on.
28 '';
29 };
30 tlsCert = lib.mkOption {
31 type = lib.types.nullOr lib.types.path;
32 example = "/var/lib/dendrite/server.cert";
33 default = null;
34 description = ''
35 The path to the TLS certificate.
36
37 ```
38 nix-shell -p dendrite --command "generate-keys --tls-cert server.crt --tls-key server.key"
39 ```
40 '';
41 };
42 tlsKey = lib.mkOption {
43 type = lib.types.nullOr lib.types.path;
44 example = "/var/lib/dendrite/server.key";
45 default = null;
46 description = ''
47 The path to the TLS key.
48
49 ```
50 nix-shell -p dendrite --command "generate-keys --tls-cert server.crt --tls-key server.key"
51 ```
52 '';
53 };
54 environmentFile = lib.mkOption {
55 type = lib.types.nullOr lib.types.path;
56 example = "/var/lib/dendrite/registration_secret";
57 default = null;
58 description = ''
59 Environment file as defined in {manpage}`systemd.exec(5)`.
60 Secrets may be passed to the service without adding them to the world-readable
61 Nix store, by specifying placeholder variables as the option value in Nix and
62 setting these variables accordingly in the environment file. Currently only used
63 for the registration secret to allow secure registration when
64 client_api.registration_disabled is true.
65
66 ```
67 # snippet of dendrite-related config
68 services.dendrite.settings.client_api.registration_shared_secret = "$REGISTRATION_SHARED_SECRET";
69 ```
70
71 ```
72 # content of the environment file
73 REGISTRATION_SHARED_SECRET=verysecretpassword
74 ```
75
76 Note that this file needs to be available on the host on which
77 `dendrite` is running.
78 '';
79 };
80 loadCredential = lib.mkOption {
81 type = lib.types.listOf lib.types.str;
82 default = [ ];
83 example = [ "private_key:/path/to/my_private_key" ];
84 description = ''
85 This can be used to pass secrets to the systemd service without adding them to
86 the nix store.
87 To use the example setting, see the example of
88 {option}`services.dendrite.settings.global.private_key`.
89 See the LoadCredential section of systemd.exec manual for more information.
90 '';
91 };
92 settings = lib.mkOption {
93 type = lib.types.submodule {
94 freeformType = settingsFormat.type;
95 options.global = {
96 server_name = lib.mkOption {
97 type = lib.types.str;
98 example = "example.com";
99 description = ''
100 The domain name of the server, with optional explicit port.
101 This is used by remote servers to connect to this server.
102 This is also the last part of your UserID.
103 '';
104 };
105 private_key = lib.mkOption {
106 type = lib.types.either lib.types.path (lib.types.strMatching "^\\$CREDENTIALS_DIRECTORY/.+");
107 example = "$CREDENTIALS_DIRECTORY/private_key";
108 description = ''
109 The path to the signing private key file, used to sign
110 requests and events.
111
112 ```
113 nix-shell -p dendrite --command "generate-keys --private-key matrix_key.pem"
114 ```
115 '';
116 };
117 trusted_third_party_id_servers = lib.mkOption {
118 type = lib.types.listOf lib.types.str;
119 example = [ "matrix.org" ];
120 default = [
121 "matrix.org"
122 "vector.im"
123 ];
124 description = ''
125 Lists of domains that the server will trust as identity
126 servers to verify third party identifiers such as phone
127 numbers and email addresses
128 '';
129 };
130 };
131 options.app_service_api.database = {
132 connection_string = lib.mkOption {
133 type = lib.types.str;
134 default = "file:federationapi.db";
135 description = ''
136 Database for the Appservice API.
137 '';
138 };
139 };
140 options.client_api = {
141 registration_disabled = lib.mkOption {
142 type = lib.types.bool;
143 default = true;
144 description = ''
145 Whether to disable user registration to the server
146 without the shared secret.
147 '';
148 };
149 };
150 options.federation_api.database = {
151 connection_string = lib.mkOption {
152 type = lib.types.str;
153 default = "file:federationapi.db";
154 description = ''
155 Database for the Federation API.
156 '';
157 };
158 };
159 options.key_server.database = {
160 connection_string = lib.mkOption {
161 type = lib.types.str;
162 default = "file:keyserver.db";
163 description = ''
164 Database for the Key Server (for end-to-end encryption).
165 '';
166 };
167 };
168 options.relay_api.database = {
169 connection_string = lib.mkOption {
170 type = lib.types.str;
171 default = "file:relayapi.db";
172 description = ''
173 Database for the Relay Server.
174 '';
175 };
176 };
177 options.media_api = {
178 database = {
179 connection_string = lib.mkOption {
180 type = lib.types.str;
181 default = "file:mediaapi.db";
182 description = ''
183 Database for the Media API.
184 '';
185 };
186 };
187 base_path = lib.mkOption {
188 type = lib.types.str;
189 default = "${workingDir}/media_store";
190 description = ''
191 Storage path for uploaded media.
192 '';
193 };
194 };
195 options.room_server.database = {
196 connection_string = lib.mkOption {
197 type = lib.types.str;
198 default = "file:roomserver.db";
199 description = ''
200 Database for the Room Server.
201 '';
202 };
203 };
204 options.sync_api.database = {
205 connection_string = lib.mkOption {
206 type = lib.types.str;
207 default = "file:syncserver.db";
208 description = ''
209 Database for the Sync API.
210 '';
211 };
212 };
213 options.sync_api.search = {
214 enabled = lib.mkEnableOption "Dendrite's full-text search engine";
215 index_path = lib.mkOption {
216 type = lib.types.str;
217 default = "${workingDir}/searchindex";
218 description = ''
219 The path the search index will be created in.
220 '';
221 };
222 language = lib.mkOption {
223 type = lib.types.str;
224 default = "en";
225 description = ''
226 The language most likely to be used on the server - used when indexing, to
227 ensure the returned results match expectations. A full list of possible languages
228 can be found at https://github.com/blevesearch/bleve/tree/master/analysis/lang
229 '';
230 };
231 };
232 options.user_api = {
233 account_database = {
234 connection_string = lib.mkOption {
235 type = lib.types.str;
236 default = "file:userapi_accounts.db";
237 description = ''
238 Database for the User API, accounts.
239 '';
240 };
241 };
242 device_database = {
243 connection_string = lib.mkOption {
244 type = lib.types.str;
245 default = "file:userapi_devices.db";
246 description = ''
247 Database for the User API, devices.
248 '';
249 };
250 };
251 };
252 options.mscs = {
253 database = {
254 connection_string = lib.mkOption {
255 type = lib.types.str;
256 default = "file:mscs.db";
257 description = ''
258 Database for exerimental MSC's.
259 '';
260 };
261 };
262 };
263 };
264 default = { };
265 description = ''
266 Configuration for dendrite, see:
267 <https://github.com/matrix-org/dendrite/blob/main/dendrite-sample.yaml>
268 for available options with which to populate settings.
269 '';
270 };
271 openRegistration = lib.mkOption {
272 type = lib.types.bool;
273 default = false;
274 description = ''
275 Allow open registration without secondary verification (reCAPTCHA).
276 '';
277 };
278 };
279
280 config = lib.mkIf cfg.enable {
281 assertions = [
282 {
283 assertion = cfg.httpsPort != null -> (cfg.tlsCert != null && cfg.tlsKey != null);
284 message = ''
285 If Dendrite is configured to use https, tlsCert and tlsKey must be provided.
286
287 nix-shell -p dendrite --command "generate-keys --tls-cert server.crt --tls-key server.key"
288 '';
289 }
290 {
291 assertion = !(cfg.settings.sync_api.search ? enable);
292 message = ''
293 The `services.dendrite.settings.sync_api.search.enable` option
294 has been renamed to `services.dendrite.settings.sync_api.search.enabled`.
295 '';
296 }
297 ];
298
299 systemd.services.dendrite = {
300 description = "Dendrite Matrix homeserver";
301 after = [
302 "network.target"
303 ];
304 wantedBy = [ "multi-user.target" ];
305 serviceConfig = {
306 Type = "simple";
307 DynamicUser = true;
308 StateDirectory = "dendrite";
309 WorkingDirectory = workingDir;
310 RuntimeDirectory = "dendrite";
311 RuntimeDirectoryMode = "0700";
312 LimitNOFILE = 65535;
313 EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
314 LoadCredential = cfg.loadCredential;
315 ExecStartPre = [
316 ''
317 ${pkgs.envsubst}/bin/envsubst \
318 -i ${configurationYaml} \
319 -o /run/dendrite/dendrite.yaml
320 ''
321 ];
322 ExecStart = lib.strings.concatStringsSep " " (
323 [
324 "${pkgs.dendrite}/bin/dendrite"
325 "--config /run/dendrite/dendrite.yaml"
326 ]
327 ++ lib.optionals (cfg.httpPort != null) [
328 "--http-bind-address :${builtins.toString cfg.httpPort}"
329 ]
330 ++ lib.optionals (cfg.httpsPort != null) [
331 "--https-bind-address :${builtins.toString cfg.httpsPort}"
332 "--tls-cert ${cfg.tlsCert}"
333 "--tls-key ${cfg.tlsKey}"
334 ]
335 ++ lib.optionals cfg.openRegistration [
336 "--really-enable-open-registration"
337 ]
338 );
339 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
340 Restart = "on-failure";
341 };
342 };
343 };
344 meta.maintainers = lib.teams.matrix.members;
345}