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