Commit | Line | Data |
---|---|---|
78cef99b | 1 | ;;; GNU Guix --- Functional package management for GNU |
bdcf0e6f | 2 | ;;; Copyright © 2017, 2018 Clément Lassieur <clement@lassieur.org> |
e7c797f3 | 3 | ;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com> |
211fe3f6 | 4 | ;;; Copyright © 2015, 2017-2020, 2022 Ludovic Courtès <ludo@gnu.org> |
36d619e8 | 5 | ;;; Copyright © 2018 Pierre-Antoine Rouby <contact@parouby.fr> |
78cef99b CL |
6 | ;;; |
7 | ;;; This file is part of GNU Guix. | |
8 | ;;; | |
9 | ;;; GNU Guix is free software; you can redistribute it and/or modify it | |
10 | ;;; under the terms of the GNU General Public License as published by | |
11 | ;;; the Free Software Foundation; either version 3 of the License, or (at | |
12 | ;;; your option) any later version. | |
13 | ;;; | |
14 | ;;; GNU Guix is distributed in the hope that it will be useful, but | |
15 | ;;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | ;;; GNU General Public License for more details. | |
18 | ;;; | |
19 | ;;; You should have received a copy of the GNU General Public License | |
20 | ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | |
21 | ||
22 | (define-module (gnu services messaging) | |
78cef99b | 23 | #:use-module (gnu packages admin) |
47d73ac4 | 24 | #:use-module (gnu packages base) |
dcad57d5 | 25 | #:use-module (gnu packages irc) |
47d73ac4 | 26 | #:use-module (gnu packages messaging) |
dcad57d5 | 27 | #:use-module (gnu packages tls) |
78cef99b CL |
28 | #:use-module (gnu services) |
29 | #:use-module (gnu services shepherd) | |
30 | #:use-module (gnu services configuration) | |
31 | #:use-module (gnu system shadow) | |
211fe3f6 LC |
32 | #:autoload (gnu build linux-container) (%namespaces) |
33 | #:use-module ((gnu system file-systems) #:select (file-system-mapping)) | |
78cef99b | 34 | #:use-module (guix gexp) |
f2bee421 | 35 | #:use-module (guix modules) |
78cef99b CL |
36 | #:use-module (guix records) |
37 | #:use-module (guix packages) | |
65a67bf7 | 38 | #:use-module (guix deprecation) |
211fe3f6 | 39 | #:use-module (guix least-authority) |
78cef99b CL |
40 | #:use-module (srfi srfi-1) |
41 | #:use-module (srfi srfi-35) | |
42 | #:use-module (ice-9 match) | |
43 | #:export (prosody-service-type | |
44 | prosody-configuration | |
45 | opaque-prosody-configuration | |
46 | ||
47 | virtualhost-configuration | |
48 | int-component-configuration | |
49 | ext-component-configuration | |
50 | ||
51 | mod-muc-configuration | |
52 | ssl-configuration | |
53 | ||
533bc514 | 54 | %default-modules-enabled |
f2bee421 LC |
55 | prosody-configuration-pidfile |
56 | ||
57 | bitlbee-configuration | |
58 | bitlbee-configuration? | |
dcad57d5 EF |
59 | bitlbee-service-type |
60 | ||
61 | quassel-configuration | |
62 | quassel-service-type)) | |
78cef99b CL |
63 | |
64 | ;;; Commentary: | |
65 | ;;; | |
66 | ;;; Messaging services. | |
67 | ;;; | |
68 | ;;; Code: | |
69 | ||
78cef99b CL |
70 | (define-syntax define-all-configurations |
71 | (lambda (stx) | |
e7c797f3 MO |
72 | (define-syntax-rule (id ctx parts ...) |
73 | "Assemble PARTS into a raw (unhygienic) identifier." | |
74 | (datum->syntax ctx (symbol-append (syntax->datum parts) ...))) | |
78cef99b CL |
75 | (define (make-pred arg) |
76 | (lambda (field target) | |
77 | (and (memq (syntax->datum target) `(common ,arg)) field))) | |
78 | (syntax-case stx () | |
8cb1a49a | 79 | ;; TODO Also handle (field-type) form, without a default. |
78cef99b CL |
80 | ((_ stem (field (field-type def) doc target) ...) |
81 | (with-syntax (((new-field-type ...) | |
82 | (map (lambda (field-type target) | |
83 | (if (and (eq? 'common (syntax->datum target)) | |
84 | (not (string-prefix? | |
85 | "maybe-" | |
86 | (symbol->string | |
87 | (syntax->datum field-type))))) | |
88 | (id #'stem #'maybe- field-type) field-type)) | |
89 | #'(field-type ...) #'(target ...))) | |
90 | ((new-def ...) | |
91 | (map (lambda (def target) | |
92 | (if (eq? 'common (syntax->datum target)) | |
ee08277a AL |
93 | ;; TODO Use the %unset-value variable, or |
94 | ;; even better just simplify this so that it | |
95 | ;; doesn't interfere with | |
96 | ;; define-configuration and define-maybe | |
97 | ;; internals. | |
6fb9759e | 98 | #''%unset-marker% def)) |
78cef99b CL |
99 | #'(def ...) #'(target ...))) |
100 | ((new-doc ...) | |
101 | (map (lambda (doc target) | |
102 | (if (eq? 'common (syntax->datum target)) | |
103 | "" doc)) | |
104 | #'(doc ...) #'(target ...)))) | |
105 | #`(begin | |
10999889 | 106 | (define #,(id #'stem #'common-fields) |
78cef99b | 107 | '(#,@(filter-map (make-pred #f) #'(field ...) #'(target ...)))) |
10999889 | 108 | (define-configuration #,(id #'stem #'prosody-configuration) |
78cef99b CL |
109 | #,@(filter-map (make-pred 'global) |
110 | #'((field (field-type def) doc) ...) | |
111 | #'(target ...))) | |
10999889 | 112 | (define-configuration #,(id #'stem #'virtualhost-configuration) |
78cef99b CL |
113 | #,@(filter-map (make-pred 'virtualhost) |
114 | #'((field (new-field-type new-def) new-doc) ...) | |
115 | #'(target ...))) | |
10999889 | 116 | (define-configuration #,(id #'stem #'int-component-configuration) |
78cef99b CL |
117 | #,@(filter-map (make-pred 'int-component) |
118 | #'((field (new-field-type new-def) new-doc) ...) | |
119 | #'(target ...))) | |
10999889 | 120 | (define-configuration #,(id #'stem #'ext-component-configuration) |
78cef99b CL |
121 | #,@(filter-map (make-pred 'ext-component) |
122 | #'((field (new-field-type new-def) new-doc) ...) | |
123 | #'(target ...))))))))) | |
124 | ||
125 | (define (uglify-field-name field-name) | |
126 | (let ((str (symbol->string field-name))) | |
127 | (string-join (string-split (if (string-suffix? "?" str) | |
128 | (substring str 0 (1- (string-length str))) | |
129 | str) | |
130 | #\-) | |
131 | "_"))) | |
132 | ||
133 | (define (serialize-field field-name val) | |
bdcf0e6f | 134 | #~(format #f "~a = ~a;\n" #$(uglify-field-name field-name) #$val)) |
78cef99b | 135 | (define (serialize-field-list field-name val) |
bdcf0e6f | 136 | (serialize-field field-name #~(format #f "{\n~@{~a;\n~}}" #$@val))) |
78cef99b CL |
137 | |
138 | (define (serialize-boolean field-name val) | |
139 | (serialize-field field-name (if val "true" "false"))) | |
140 | (define-maybe boolean) | |
141 | ||
142 | (define (string-or-boolean? val) | |
143 | (or (string? val) (boolean? val))) | |
144 | (define (serialize-string-or-boolean field-name val) | |
145 | (if (string? val) | |
146 | (serialize-string field-name val) | |
147 | (serialize-boolean field-name val))) | |
148 | ||
149 | (define (non-negative-integer? val) | |
150 | (and (exact-integer? val) (not (negative? val)))) | |
151 | (define (serialize-non-negative-integer field-name val) | |
bdcf0e6f | 152 | (serialize-field field-name (number->string val))) |
78cef99b CL |
153 | (define-maybe non-negative-integer) |
154 | ||
155 | (define (non-negative-integer-list? val) | |
156 | (and (list? val) (and-map non-negative-integer? val))) | |
157 | (define (serialize-non-negative-integer-list field-name val) | |
bdcf0e6f | 158 | (serialize-field-list field-name (map number->string val))) |
78cef99b CL |
159 | (define-maybe non-negative-integer-list) |
160 | ||
161 | (define (enclose-quotes s) | |
bdcf0e6f | 162 | #~(string-append "\"" #$s "\"")) |
78cef99b CL |
163 | (define (serialize-string field-name val) |
164 | (serialize-field field-name (enclose-quotes val))) | |
165 | (define-maybe string) | |
166 | ||
167 | (define (string-list? val) | |
168 | (and (list? val) | |
169 | (and-map (lambda (x) | |
170 | (and (string? x) (not (string-index x #\,)))) | |
171 | val))) | |
172 | (define (serialize-string-list field-name val) | |
173 | (serialize-field-list field-name (map enclose-quotes val))) | |
174 | (define-maybe string-list) | |
175 | ||
176 | (define (module-list? val) | |
177 | (string-list? val)) | |
178 | (define (serialize-module-list field-name val) | |
5cc6dcd7 | 179 | (serialize-string-list field-name val)) |
78cef99b CL |
180 | (define-maybe module-list) |
181 | ||
182 | (define (file-name? val) | |
183 | (and (string? val) | |
184 | (string-prefix? "/" val))) | |
185 | (define (serialize-file-name field-name val) | |
186 | (serialize-string field-name val)) | |
187 | (define-maybe file-name) | |
188 | ||
189 | (define (file-name-list? val) | |
190 | (and (list? val) (and-map file-name? val))) | |
191 | (define (serialize-file-name-list field-name val) | |
192 | (serialize-string-list field-name val)) | |
9ef6d80e | 193 | (define-maybe file-name-list) |
78cef99b | 194 | |
bdcf0e6f CL |
195 | (define (file-object? val) |
196 | (or (file-like? val) (file-name? val))) | |
197 | (define (serialize-file-object field-name val) | |
198 | (serialize-string field-name val)) | |
199 | (define-maybe file-object) | |
200 | ||
201 | (define (file-object-list? val) | |
202 | (and (list? val) (and-map file-object? val))) | |
203 | (define (serialize-file-object-list field-name val) | |
204 | (serialize-string-list field-name val)) | |
9ef6d80e | 205 | (define-maybe file-object-list) |
bdcf0e6f | 206 | |
274b9500 | 207 | (define (raw-content? val) |
ee08277a | 208 | (maybe-value-set? val)) |
274b9500 | 209 | (define (serialize-raw-content field-name val) |
bdcf0e6f | 210 | val) |
274b9500 CL |
211 | (define-maybe raw-content) |
212 | ||
78cef99b CL |
213 | (define-configuration mod-muc-configuration |
214 | (name | |
215 | (string "Prosody Chatrooms") | |
216 | "The name to return in service discovery responses.") | |
217 | ||
218 | (restrict-room-creation | |
219 | (string-or-boolean #f) | |
220 | "If @samp{#t}, this will only allow admins to create new chatrooms. | |
221 | Otherwise anyone can create a room. The value @samp{\"local\"} restricts room | |
222 | creation to users on the service's parent domain. E.g. @samp{user@@example.com} | |
223 | can create rooms on @samp{rooms.example.com}. The value @samp{\"admin\"} | |
224 | restricts to service administrators only.") | |
225 | ||
226 | (max-history-messages | |
227 | (non-negative-integer 20) | |
228 | "Maximum number of history messages that will be sent to the member that has | |
229 | just joined the room.")) | |
230 | (define (serialize-mod-muc-configuration field-name val) | |
231 | (serialize-configuration val mod-muc-configuration-fields)) | |
232 | (define-maybe mod-muc-configuration) | |
233 | ||
234 | (define-configuration ssl-configuration | |
235 | (protocol | |
8cb1a49a | 236 | maybe-string |
78cef99b CL |
237 | "This determines what handshake to use.") |
238 | ||
239 | (key | |
8cb1a49a | 240 | maybe-file-name |
5cc6dcd7 | 241 | "Path to your private key file.") |
78cef99b CL |
242 | |
243 | (certificate | |
8cb1a49a | 244 | maybe-file-name |
5cc6dcd7 | 245 | "Path to your certificate file.") |
78cef99b CL |
246 | |
247 | (capath | |
bdcf0e6f | 248 | (file-object "/etc/ssl/certs") |
78cef99b CL |
249 | "Path to directory containing root certificates that you wish Prosody to |
250 | trust when verifying the certificates of remote servers.") | |
251 | ||
252 | (cafile | |
8cb1a49a | 253 | maybe-file-object |
78cef99b CL |
254 | "Path to a file containing root certificates that you wish Prosody to trust. |
255 | Similar to @code{capath} but with all certificates concatenated together.") | |
256 | ||
257 | (verify | |
8cb1a49a | 258 | maybe-string-list |
78cef99b CL |
259 | "A list of verification options (these mostly map to OpenSSL's |
260 | @code{set_verify()} flags).") | |
261 | ||
262 | (options | |
8cb1a49a | 263 | maybe-string-list |
78cef99b CL |
264 | "A list of general options relating to SSL/TLS. These map to OpenSSL's |
265 | @code{set_options()}. For a full list of options available in LuaSec, see the | |
266 | LuaSec source.") | |
267 | ||
268 | (depth | |
8cb1a49a | 269 | maybe-non-negative-integer |
78cef99b CL |
270 | "How long a chain of certificate authorities to check when looking for a |
271 | trusted root certificate.") | |
272 | ||
273 | (ciphers | |
8cb1a49a | 274 | maybe-string |
78cef99b CL |
275 | "An OpenSSL cipher string. This selects what ciphers Prosody will offer to |
276 | clients, and in what order.") | |
277 | ||
278 | (dhparam | |
8cb1a49a | 279 | maybe-file-name |
78cef99b CL |
280 | "A path to a file containing parameters for Diffie-Hellman key exchange. You |
281 | can create such a file with: | |
282 | @code{openssl dhparam -out /etc/prosody/certs/dh-2048.pem 2048}") | |
283 | ||
284 | (curve | |
8cb1a49a | 285 | maybe-string |
78cef99b CL |
286 | "Curve for Elliptic curve Diffie-Hellman. Prosody's default is |
287 | @samp{\"secp384r1\"}.") | |
288 | ||
289 | (verifyext | |
8cb1a49a | 290 | maybe-string-list |
78cef99b CL |
291 | "A list of \"extra\" verification options.") |
292 | ||
293 | (password | |
8cb1a49a | 294 | maybe-string |
78cef99b CL |
295 | "Password for encrypted private keys.")) |
296 | (define (serialize-ssl-configuration field-name val) | |
bdcf0e6f CL |
297 | #~(format #f "ssl = {\n~a};\n" |
298 | #$(serialize-configuration val ssl-configuration-fields))) | |
78cef99b CL |
299 | (define-maybe ssl-configuration) |
300 | ||
301 | (define %default-modules-enabled | |
302 | '("roster" | |
303 | "saslauth" | |
304 | "tls" | |
305 | "dialback" | |
306 | "disco" | |
5cc6dcd7 | 307 | "carbons" |
78cef99b | 308 | "private" |
5cc6dcd7 | 309 | "blocklist" |
78cef99b CL |
310 | "vcard" |
311 | "version" | |
312 | "uptime" | |
313 | "time" | |
314 | "ping" | |
315 | "pep" | |
316 | "register" | |
317 | "admin_adhoc")) | |
318 | ||
319 | ;; Guile bug. Use begin wrapper, because otherwise virtualhost-configuration | |
320 | ;; is assumed to be a function. See | |
321 | ;; https://www.gnu.org/software/guile/manual/html_node/R6RS-Incompatibilities.html | |
322 | (begin | |
323 | (define (virtualhost-configuration-list? val) | |
324 | (and (list? val) (and-map virtualhost-configuration? val))) | |
325 | (define (serialize-virtualhost-configuration-list l) | |
bdcf0e6f CL |
326 | #~(string-append |
327 | #$@(map (lambda (val) | |
328 | (serialize-virtualhost-configuration val)) l))) | |
78cef99b CL |
329 | |
330 | (define (int-component-configuration-list? val) | |
331 | (and (list? val) (and-map int-component-configuration? val))) | |
332 | (define (serialize-int-component-configuration-list l) | |
bdcf0e6f CL |
333 | #~(string-append |
334 | #$@(map (lambda (val) | |
335 | (serialize-int-component-configuration val)) l))) | |
78cef99b CL |
336 | |
337 | (define (ext-component-configuration-list? val) | |
338 | (and (list? val) (and-map ext-component-configuration? val))) | |
339 | (define (serialize-ext-component-configuration-list l) | |
bdcf0e6f CL |
340 | #~(string-append |
341 | #$@(map (lambda (val) | |
342 | (serialize-ext-component-configuration val)) l))) | |
78cef99b CL |
343 | |
344 | (define-all-configurations prosody-configuration | |
345 | (prosody | |
892f1b72 | 346 | (file-like prosody) |
78cef99b CL |
347 | "The Prosody package." |
348 | global) | |
349 | ||
350 | (data-path | |
351 | (file-name "/var/lib/prosody") | |
352 | "Location of the Prosody data storage directory. See | |
7459cb93 | 353 | @url{https://prosody.im/doc/configure}." |
78cef99b CL |
354 | global) |
355 | ||
356 | (plugin-paths | |
bdcf0e6f | 357 | (file-object-list '()) |
78cef99b | 358 | "Additional plugin directories. They are searched in all the specified |
7459cb93 | 359 | paths in order. See @url{https://prosody.im/doc/plugins_directory}." |
78cef99b CL |
360 | global) |
361 | ||
5cc6dcd7 CL |
362 | (certificates |
363 | (file-name "/etc/prosody/certs") | |
364 | "Every virtual host and component needs a certificate so that clients and | |
365 | servers can securely verify its identity. Prosody will automatically load | |
366 | certificates/keys from the directory specified here." | |
367 | global) | |
368 | ||
78cef99b CL |
369 | (admins |
370 | (string-list '()) | |
371 | "This is a list of accounts that are admins for the server. Note that you | |
7459cb93 TGR |
372 | must create the accounts separately. See @url{https://prosody.im/doc/admins} and |
373 | @url{https://prosody.im/doc/creating_accounts}. | |
78cef99b CL |
374 | Example: @code{(admins '(\"user1@@example.com\" \"user2@@example.net\"))}" |
375 | common) | |
376 | ||
377 | (use-libevent? | |
378 | (boolean #f) | |
379 | "Enable use of libevent for better performance under high load. See | |
7459cb93 | 380 | @url{https://prosody.im/doc/libevent}." |
78cef99b CL |
381 | common) |
382 | ||
383 | (modules-enabled | |
384 | (module-list %default-modules-enabled) | |
385 | "This is the list of modules Prosody will load on startup. It looks for | |
386 | @code{mod_modulename.lua} in the plugins folder, so make sure that exists too. | |
19ff1f26 | 387 | Documentation on modules can be found at: |
7459cb93 | 388 | @url{https://prosody.im/doc/modules}." |
78cef99b CL |
389 | common) |
390 | ||
391 | (modules-disabled | |
392 | (string-list '()) | |
393 | "@samp{\"offline\"}, @samp{\"c2s\"} and @samp{\"s2s\"} are auto-loaded, but | |
394 | should you want to disable them then add them to this list." | |
395 | common) | |
396 | ||
397 | (groups-file | |
bdcf0e6f | 398 | (file-object "/var/lib/prosody/sharedgroups.txt") |
78cef99b CL |
399 | "Path to a text file where the shared groups are defined. If this path is |
400 | empty then @samp{mod_groups} does nothing. See | |
7459cb93 | 401 | @url{https://prosody.im/doc/modules/mod_groups}." |
78cef99b CL |
402 | common) |
403 | ||
404 | (allow-registration? | |
405 | (boolean #f) | |
406 | "Disable account creation by default, for security. See | |
7459cb93 | 407 | @url{https://prosody.im/doc/creating_accounts}." |
78cef99b CL |
408 | common) |
409 | ||
410 | (ssl | |
411 | (maybe-ssl-configuration (ssl-configuration)) | |
412 | "These are the SSL/TLS-related settings. Most of them are disabled so to | |
413 | use Prosody's defaults. If you do not completely understand these options, do | |
414 | not add them to your config, it is easy to lower the security of your server | |
7459cb93 | 415 | using them. See @url{https://prosody.im/doc/advanced_ssl_config}." |
78cef99b CL |
416 | common) |
417 | ||
418 | (c2s-require-encryption? | |
419 | (boolean #f) | |
420 | "Whether to force all client-to-server connections to be encrypted or not. | |
7459cb93 | 421 | See @url{https://prosody.im/doc/modules/mod_tls}." |
78cef99b CL |
422 | common) |
423 | ||
e30038da CL |
424 | (disable-sasl-mechanisms |
425 | (string-list '("DIGEST-MD5")) | |
426 | "Set of mechanisms that will never be offered. See | |
427 | @url{https://prosody.im/doc/modules/mod_saslauth}." | |
428 | common) | |
429 | ||
78cef99b CL |
430 | (s2s-require-encryption? |
431 | (boolean #f) | |
432 | "Whether to force all server-to-server connections to be encrypted or not. | |
7459cb93 | 433 | See @url{https://prosody.im/doc/modules/mod_tls}." |
78cef99b CL |
434 | common) |
435 | ||
436 | (s2s-secure-auth? | |
437 | (boolean #f) | |
438 | "Whether to require encryption and certificate authentication. This | |
439 | provides ideal security, but requires servers you communicate with to support | |
440 | encryption AND present valid, trusted certificates. See | |
7459cb93 | 441 | @url{https://prosody.im/doc/s2s#security}." |
78cef99b CL |
442 | common) |
443 | ||
444 | (s2s-insecure-domains | |
445 | (string-list '()) | |
446 | "Many servers don't support encryption or have invalid or self-signed | |
447 | certificates. You can list domains here that will not be required to | |
448 | authenticate using certificates. They will be authenticated using DNS. See | |
7459cb93 | 449 | @url{https://prosody.im/doc/s2s#security}." |
78cef99b CL |
450 | common) |
451 | ||
452 | (s2s-secure-domains | |
453 | (string-list '()) | |
454 | "Even if you leave @code{s2s-secure-auth?} disabled, you can still require | |
455 | valid certificates for some domains by specifying a list here. See | |
7459cb93 | 456 | @url{https://prosody.im/doc/s2s#security}." |
78cef99b CL |
457 | common) |
458 | ||
459 | (authentication | |
460 | (string "internal_plain") | |
461 | "Select the authentication backend to use. The default provider stores | |
462 | passwords in plaintext and uses Prosody's configured data storage to store the | |
463 | authentication data. If you do not trust your server please see | |
7459cb93 | 464 | @url{https://prosody.im/doc/modules/mod_auth_internal_hashed} for information |
78cef99b | 465 | about using the hashed backend. See also |
7459cb93 | 466 | @url{https://prosody.im/doc/authentication}" |
78cef99b CL |
467 | common) |
468 | ||
469 | ;; TODO: Handle more complicated log structures. | |
470 | (log | |
471 | (maybe-string "*syslog") | |
472 | "Set logging options. Advanced logging configuration is not yet supported | |
59e80445 | 473 | by the Prosody service. See @url{https://prosody.im/doc/logging}." |
78cef99b CL |
474 | common) |
475 | ||
476 | (pidfile | |
477 | (file-name "/var/run/prosody/prosody.pid") | |
7459cb93 | 478 | "File to write pid in. See @url{https://prosody.im/doc/modules/mod_posix}." |
78cef99b CL |
479 | global) |
480 | ||
1f6f1a07 | 481 | (http-max-content-size |
ee08277a | 482 | (maybe-non-negative-integer %unset-value) |
1f6f1a07 CL |
483 | "Maximum allowed size of the HTTP body (in bytes)." |
484 | common) | |
485 | ||
f59de6be | 486 | (http-external-url |
ee08277a | 487 | (maybe-string %unset-value) |
f59de6be CL |
488 | "Some modules expose their own URL in various ways. This URL is built |
489 | from the protocol, host and port used. If Prosody sits behind a proxy, the | |
490 | public URL will be @code{http-external-url} instead. See | |
491 | @url{https://prosody.im/doc/http#external_url}." | |
492 | common) | |
493 | ||
78cef99b CL |
494 | (virtualhosts |
495 | (virtualhost-configuration-list | |
496 | (list (virtualhost-configuration | |
497 | (domain "localhost")))) | |
498 | "A host in Prosody is a domain on which user accounts can be created. For | |
499 | example if you want your users to have addresses like | |
500 | @samp{\"john.smith@@example.com\"} then you need to add a host | |
501 | @samp{\"example.com\"}. All options in this list will apply only to this host. | |
502 | ||
503 | Note: the name \"virtual\" host is used in configuration to avoid confusion with | |
504 | the actual physical host that Prosody is installed on. A single Prosody | |
505 | instance can serve many domains, each one defined as a VirtualHost entry in | |
506 | Prosody's configuration. Conversely a server that hosts a single domain would | |
507 | have just one VirtualHost entry. | |
508 | ||
7459cb93 | 509 | See @url{https://prosody.im/doc/configure#virtual_host_settings}." |
78cef99b CL |
510 | global) |
511 | ||
512 | (int-components | |
513 | (int-component-configuration-list '()) | |
514 | "Components are extra services on a server which are available to clients, | |
515 | usually on a subdomain of the main server (such as | |
516 | @samp{\"mycomponent.example.com\"}). Example components might be chatroom | |
517 | servers, user directories, or gateways to other protocols. | |
518 | ||
519 | Internal components are implemented with Prosody-specific plugins. To add an | |
520 | internal component, you simply fill the hostname field, and the plugin you wish | |
521 | to use for the component. | |
522 | ||
7459cb93 | 523 | See @url{https://prosody.im/doc/components}." |
78cef99b CL |
524 | global) |
525 | ||
526 | (ext-components | |
527 | (ext-component-configuration-list '()) | |
528 | "External components use XEP-0114, which most standalone components | |
529 | support. To add an external component, you simply fill the hostname field. See | |
7459cb93 | 530 | @url{https://prosody.im/doc/components}." |
78cef99b CL |
531 | global) |
532 | ||
533 | (component-secret | |
534 | (string (configuration-missing-field 'ext-component 'component-secret)) | |
535 | "Password which the component will use to log in." | |
536 | ext-component) | |
537 | ||
538 | (component-ports | |
539 | (non-negative-integer-list '(5347)) | |
540 | "Port(s) Prosody listens on for component connections." | |
541 | global) | |
542 | ||
543 | (component-interface | |
544 | (string "127.0.0.1") | |
545 | "Interface Prosody listens on for component connections." | |
546 | global) | |
547 | ||
548 | (domain | |
549 | (string (configuration-missing-field 'virtualhost 'domain)) | |
550 | "Domain you wish Prosody to serve." | |
551 | virtualhost) | |
552 | ||
553 | (hostname | |
554 | (string (configuration-missing-field 'int-component 'hostname)) | |
555 | "Hostname of the component." | |
556 | int-component) | |
557 | ||
558 | (plugin | |
559 | (string (configuration-missing-field 'int-component 'plugin)) | |
560 | "Plugin you wish to use for the component." | |
561 | int-component) | |
562 | ||
563 | (mod-muc | |
ee08277a | 564 | (maybe-mod-muc-configuration %unset-value) |
78cef99b CL |
565 | "Multi-user chat (MUC) is Prosody's module for allowing you to create |
566 | hosted chatrooms/conferences for XMPP users. | |
567 | ||
568 | General information on setting up and using multi-user chatrooms can be found | |
7459cb93 | 569 | in the \"Chatrooms\" documentation (@url{https://prosody.im/doc/chatrooms}), |
78cef99b CL |
570 | which you should read if you are new to XMPP chatrooms. |
571 | ||
7459cb93 | 572 | See also @url{https://prosody.im/doc/modules/mod_muc}." |
78cef99b CL |
573 | int-component) |
574 | ||
575 | (hostname | |
576 | (string (configuration-missing-field 'ext-component 'hostname)) | |
577 | "Hostname of the component." | |
274b9500 CL |
578 | ext-component) |
579 | ||
580 | (raw-content | |
ee08277a | 581 | (maybe-raw-content %unset-value) |
274b9500 CL |
582 | "Raw content that will be added to the configuration file." |
583 | common))) | |
78cef99b CL |
584 | |
585 | ;; Serialize Virtualhost line first. | |
586 | (define (serialize-virtualhost-configuration config) | |
587 | (define (rest? field) | |
588 | (not (memq (configuration-field-name field) | |
589 | '(domain)))) | |
590 | (let ((domain (virtualhost-configuration-domain config)) | |
591 | (rest (filter rest? virtualhost-configuration-fields))) | |
bdcf0e6f CL |
592 | #~(string-append |
593 | #$(format #f "VirtualHost \"~a\"\n" domain) | |
594 | #$(serialize-configuration config rest)))) | |
78cef99b CL |
595 | |
596 | ;; Serialize Component line first. | |
597 | (define (serialize-int-component-configuration config) | |
598 | (define (rest? field) | |
599 | (not (memq (configuration-field-name field) | |
600 | '(hostname plugin)))) | |
601 | (let ((hostname (int-component-configuration-hostname config)) | |
602 | (plugin (int-component-configuration-plugin config)) | |
603 | (rest (filter rest? int-component-configuration-fields))) | |
bdcf0e6f CL |
604 | #~(string-append |
605 | #$(format #f "Component \"~a\" \"~a\"\n" hostname plugin) | |
606 | #$(serialize-configuration config rest)))) | |
78cef99b CL |
607 | |
608 | ;; Serialize Component line first. | |
609 | (define (serialize-ext-component-configuration config) | |
610 | (define (rest? field) | |
611 | (not (memq (configuration-field-name field) | |
612 | '(hostname)))) | |
613 | (let ((hostname (ext-component-configuration-hostname config)) | |
614 | (rest (filter rest? ext-component-configuration-fields))) | |
bdcf0e6f CL |
615 | #~(string-append |
616 | #$(format #f "Component \"~a\"\n" hostname) | |
617 | #$(serialize-configuration config rest)))) | |
78cef99b CL |
618 | |
619 | ;; Serialize virtualhosts and components last. | |
620 | (define (serialize-prosody-configuration config) | |
621 | (define (rest? field) | |
622 | (not (memq (configuration-field-name field) | |
623 | '(virtualhosts int-components ext-components)))) | |
bdcf0e6f CL |
624 | #~(string-append |
625 | #$(let ((rest (filter rest? prosody-configuration-fields))) | |
626 | (serialize-configuration config rest)) | |
627 | #$(serialize-virtualhost-configuration-list | |
628 | (prosody-configuration-virtualhosts config)) | |
629 | #$(serialize-int-component-configuration-list | |
630 | (prosody-configuration-int-components config)) | |
631 | #$(serialize-ext-component-configuration-list | |
632 | (prosody-configuration-ext-components config)))) | |
78cef99b CL |
633 | |
634 | (define-configuration opaque-prosody-configuration | |
635 | (prosody | |
892f1b72 | 636 | (file-like prosody) |
78cef99b CL |
637 | "The prosody package.") |
638 | ||
639 | (prosody.cfg.lua | |
640 | (string (configuration-missing-field 'opaque-prosody-configuration | |
641 | 'prosody.cfg.lua)) | |
642 | "The contents of the @code{prosody.cfg.lua} to use.")) | |
643 | ||
644 | (define (prosody-shepherd-service config) | |
645 | "Return a <shepherd-service> for Prosody with CONFIG." | |
646 | (let* ((prosody (if (opaque-prosody-configuration? config) | |
647 | (opaque-prosody-configuration-prosody config) | |
648 | (prosody-configuration-prosody config))) | |
649 | (prosodyctl-bin (file-append prosody "/bin/prosodyctl")) | |
fdbca05d | 650 | (pid-file (prosody-configuration-pidfile config)) |
78cef99b CL |
651 | (prosodyctl-action (lambda args |
652 | #~(lambda _ | |
fdbca05d CL |
653 | (invoke #$prosodyctl-bin #$@args) |
654 | (match '#$args | |
655 | (("start") | |
656 | (call-with-input-file #$pid-file read)) | |
657 | (_ #t)))))) | |
78cef99b CL |
658 | (list (shepherd-service |
659 | (documentation "Run the Prosody XMPP server") | |
533bc514 | 660 | (provision '(prosody xmpp-daemon)) |
78cef99b | 661 | (requirement '(networking syslogd user-processes)) |
fdbca05d CL |
662 | (modules `((ice-9 match) |
663 | ,@%default-modules)) | |
78cef99b CL |
664 | (start (prosodyctl-action "start")) |
665 | (stop (prosodyctl-action "stop")))))) | |
666 | ||
667 | (define %prosody-accounts | |
668 | (list (user-group (name "prosody") (system? #t)) | |
669 | (user-account | |
670 | (name "prosody") | |
671 | (group "prosody") | |
672 | (system? #t) | |
673 | (comment "Prosody daemon user") | |
674 | (home-directory "/var/empty") | |
675 | (shell (file-append shadow "/sbin/nologin"))))) | |
676 | ||
677 | (define (prosody-activation config) | |
678 | "Return the activation gexp for CONFIG." | |
679 | (let* ((config-dir "/etc/prosody") | |
680 | (default-certs-dir "/etc/prosody/certs") | |
681 | (data-path (prosody-configuration-data-path config)) | |
682 | (pidfile-dir (dirname (prosody-configuration-pidfile config))) | |
bdcf0e6f CL |
683 | (config-str (if (opaque-prosody-configuration? config) |
684 | (opaque-prosody-configuration-prosody.cfg.lua config) | |
685 | #~(begin | |
686 | (use-modules (ice-9 format)) | |
687 | #$(serialize-prosody-configuration config)))) | |
688 | (config-file (mixed-text-file "prosody.cfg.lua" config-str))) | |
78cef99b | 689 | #~(begin |
87508d9a | 690 | (use-modules (guix build utils)) |
78cef99b CL |
691 | (define %user (getpw "prosody")) |
692 | ||
693 | (mkdir-p #$config-dir) | |
694 | (chown #$config-dir (passwd:uid %user) (passwd:gid %user)) | |
695 | (copy-file #$config-file (string-append #$config-dir | |
696 | "/prosody.cfg.lua")) | |
697 | ||
698 | (mkdir-p #$default-certs-dir) | |
699 | (chown #$default-certs-dir (passwd:uid %user) (passwd:gid %user)) | |
700 | (chmod #$default-certs-dir #o750) | |
701 | ||
702 | (mkdir-p #$data-path) | |
703 | (chown #$data-path (passwd:uid %user) (passwd:gid %user)) | |
704 | (chmod #$data-path #o750) | |
705 | ||
706 | (mkdir-p #$pidfile-dir) | |
707 | (chown #$pidfile-dir (passwd:uid %user) (passwd:gid %user))))) | |
708 | ||
709 | (define prosody-service-type | |
710 | (service-type (name 'prosody) | |
711 | (extensions | |
712 | (list (service-extension shepherd-root-service-type | |
713 | prosody-shepherd-service) | |
714 | (service-extension account-service-type | |
715 | (const %prosody-accounts)) | |
716 | (service-extension activation-service-type | |
9c6a461c | 717 | prosody-activation))) |
cb7c80f6 CL |
718 | (default-value (prosody-configuration |
719 | (virtualhosts | |
720 | (list | |
721 | (virtualhost-configuration | |
722 | (domain "localhost")))))) | |
9c6a461c CL |
723 | (description |
724 | "Run Prosody, a modern XMPP communication server."))) | |
78cef99b CL |
725 | |
726 | ;; A little helper to make it easier to document all those fields. | |
727 | (define (generate-documentation) | |
728 | (define documentation | |
729 | `((prosody-configuration | |
730 | ,prosody-configuration-fields | |
731 | (ssl ssl-configuration) | |
732 | (virtualhosts virtualhost-configuration) | |
733 | (int-components int-component-configuration) | |
734 | (ext-components ext-component-configuration)) | |
735 | (ssl-configuration ,ssl-configuration-fields) | |
736 | (int-component-configuration ,int-component-configuration-fields | |
737 | (mod-muc mod-muc-configuration)) | |
738 | (ext-component-configuration ,ext-component-configuration-fields) | |
739 | (mod-muc-configuration ,mod-muc-configuration-fields) | |
740 | (virtualhost-configuration ,virtualhost-configuration-fields) | |
741 | (opaque-prosody-configuration ,opaque-prosody-configuration-fields))) | |
742 | (define (generate configuration-name) | |
743 | (match (assq-ref documentation configuration-name) | |
744 | ((fields . sub-documentation) | |
745 | (format #t "\nAvailable @code{~a} fields are:\n\n" configuration-name) | |
746 | (when (memq configuration-name | |
747 | '(virtualhost-configuration | |
748 | int-component-configuration | |
749 | ext-component-configuration)) | |
750 | (format #t "all these @code{prosody-configuration} fields: ~a, plus:\n" | |
751 | (string-join (map (lambda (s) | |
752 | (format #f "@code{~a}" s)) common-fields) | |
753 | ", "))) | |
754 | (for-each | |
755 | (lambda (f) | |
756 | (let ((field-name (configuration-field-name f)) | |
757 | (field-type (configuration-field-type f)) | |
758 | (field-docs (string-trim-both | |
759 | (configuration-field-documentation f))) | |
760 | (default (catch #t | |
761 | (configuration-field-default-value-thunk f) | |
762 | (lambda _ 'nope)))) | |
763 | (define (escape-chars str chars escape) | |
764 | (with-output-to-string | |
765 | (lambda () | |
766 | (string-for-each (lambda (c) | |
767 | (when (char-set-contains? chars c) | |
768 | (display escape)) | |
769 | (display c)) | |
770 | str)))) | |
771 | (define (show-default? val) | |
19ff1f26 | 772 | (or (string? val) (number? val) (boolean? val) |
78cef99b CL |
773 | (and (list? val) (and-map show-default? val)))) |
774 | (format #t "@deftypevr {@code{~a} parameter} ~a ~a\n~a\n" | |
775 | configuration-name field-type field-name field-docs) | |
776 | (when (show-default? default) | |
777 | (format #t "Defaults to @samp{~a}.\n" | |
778 | (escape-chars (format #f "~s" default) | |
779 | (char-set #\@ #\{ #\}) | |
780 | #\@))) | |
781 | (for-each generate (or (assq-ref sub-documentation field-name) '())) | |
782 | (format #t "@end deftypevr\n\n"))) | |
783 | (filter (lambda (f) | |
784 | (not (string=? "" (configuration-field-documentation f)))) | |
785 | fields))))) | |
786 | (generate 'prosody-configuration) | |
787 | (format #t "It could be that you just want to get a @code{prosody.cfg.lua} | |
788 | up and running. In that case, you can pass an | |
789 | @code{opaque-prosody-configuration} record as the value of | |
790 | @code{prosody-service-type}. As its name indicates, an opaque configuration | |
791 | does not have easy reflective capabilities.") | |
792 | (generate 'opaque-prosody-configuration) | |
793 | (format #t "For example, if your @code{prosody.cfg.lua} is just the empty | |
794 | string, you could instantiate a prosody service like this: | |
795 | ||
796 | @example | |
797 | (service prosody-service-type | |
798 | (opaque-prosody-configuration | |
799 | (prosody.cfg.lua \"\"))) | |
800 | @end example")) | |
f2bee421 LC |
801 | |
802 | \f | |
803 | ;;; | |
804 | ;;; BitlBee. | |
805 | ;;; | |
806 | ||
807 | (define-record-type* <bitlbee-configuration> | |
808 | bitlbee-configuration make-bitlbee-configuration | |
809 | bitlbee-configuration? | |
810 | (bitlbee bitlbee-configuration-bitlbee | |
811 | (default bitlbee)) | |
812 | (interface bitlbee-configuration-interface | |
813 | (default "127.0.0.1")) | |
814 | (port bitlbee-configuration-port | |
815 | (default 6667)) | |
36d619e8 PAR |
816 | (plugins bitlbee-plugins |
817 | (default '())) | |
f2bee421 LC |
818 | (extra-settings bitlbee-configuration-extra-settings |
819 | (default ""))) | |
820 | ||
821 | (define bitlbee-shepherd-service | |
822 | (match-lambda | |
36d619e8 PAR |
823 | (($ <bitlbee-configuration> bitlbee interface port |
824 | plugins extra-settings) | |
1b157bbe LC |
825 | (let* ((plugins (directory-union "bitlbee-plugins" plugins)) |
826 | (conf (mixed-text-file "bitlbee.conf" | |
36d619e8 | 827 | " |
f2bee421 LC |
828 | [settings] |
829 | User = bitlbee | |
830 | ConfigDir = /var/lib/bitlbee | |
831 | DaemonInterface = " interface " | |
832 | DaemonPort = " (number->string port) " | |
1b157bbe | 833 | PluginDir = " plugins "/lib/bitlbee |
211fe3f6 LC |
834 | " extra-settings)) |
835 | (bitlbee* (least-authority-wrapper | |
836 | (file-append bitlbee "/sbin/bitlbee") | |
837 | #:name "bitlbee" | |
6075d251 | 838 | #:preserved-environment-variables |
47d73ac4 | 839 | '("PURPLE_PLUGIN_PATH" "GUIX_LOCPATH" "LC_ALL") |
211fe3f6 LC |
840 | #:mappings (list (file-system-mapping |
841 | (source "/var/lib/bitlbee") | |
842 | (target source) | |
843 | (writable? #t)) | |
47d73ac4 LC |
844 | (file-system-mapping |
845 | (source "/run/current-system/locale") | |
846 | (target source)) | |
211fe3f6 LC |
847 | (file-system-mapping |
848 | (source conf) | |
849 | (target conf))) | |
850 | #:namespaces (delq 'net %namespaces)))) | |
f2bee421 LC |
851 | |
852 | (with-imported-modules (source-module-closure | |
853 | '((gnu build shepherd) | |
854 | (gnu system file-systems))) | |
855 | (list (shepherd-service | |
856 | (provision '(bitlbee)) | |
857 | ||
858 | ;; Note: If networking is not up, then /etc/resolv.conf | |
859 | ;; doesn't get mapped in the container, hence the dependency | |
860 | ;; on 'networking'. | |
861 | (requirement '(user-processes networking)) | |
862 | ||
863 | (modules '((gnu build shepherd) | |
864 | (gnu system file-systems))) | |
211fe3f6 LC |
865 | (start #~(if (defined? 'make-inetd-constructor) |
866 | ||
867 | (make-inetd-constructor | |
ecfcdff2 | 868 | (list #$bitlbee* "-I" "-c" #$conf) |
211fe3f6 LC |
869 | (addrinfo:addr |
870 | (car (getaddrinfo #$interface | |
871 | #$(number->string port) | |
872 | (logior AI_NUMERICHOST | |
873 | AI_NUMERICSERV)))) | |
874 | #:service-name-stem "bitlbee" | |
ecfcdff2 | 875 | #:user "bitlbee" #:group "bitlbee" |
211fe3f6 LC |
876 | |
877 | ;; Allow 'bitlbee-purple' to use libpurple plugins. | |
878 | #:environment-variables | |
879 | (list (string-append "PURPLE_PLUGIN_PATH=" | |
47d73ac4 LC |
880 | #$plugins "/lib/purple-2") |
881 | "GUIX_LOCPATH=/run/current-system/locale")) | |
211fe3f6 LC |
882 | |
883 | (make-forkexec-constructor/container | |
884 | (list #$(file-append bitlbee "/sbin/bitlbee") | |
885 | "-n" "-F" "-u" "bitlbee" "-c" #$conf) | |
886 | ||
887 | ;; Allow 'bitlbee-purple' to use libpurple plugins. | |
888 | #:environment-variables | |
889 | (list (string-append "PURPLE_PLUGIN_PATH=" | |
890 | #$plugins "/lib/purple-2")) | |
891 | ||
892 | #:pid-file "/var/run/bitlbee.pid" | |
893 | #:mappings (list (file-system-mapping | |
894 | (source "/var/lib/bitlbee") | |
895 | (target source) | |
896 | (writable? #t)))))) | |
2a37f174 LC |
897 | (stop #~(if (defined? 'make-inetd-destructor) |
898 | (make-inetd-destructor) | |
899 | (make-kill-destructor)))))))))) | |
f2bee421 LC |
900 | |
901 | (define %bitlbee-accounts | |
902 | ;; User group and account to run BitlBee. | |
903 | (list (user-group (name "bitlbee") (system? #t)) | |
904 | (user-account | |
905 | (name "bitlbee") | |
906 | (group "bitlbee") | |
907 | (system? #t) | |
908 | (comment "BitlBee daemon user") | |
909 | (home-directory "/var/empty") | |
910 | (shell (file-append shadow "/sbin/nologin"))))) | |
911 | ||
912 | (define %bitlbee-activation | |
913 | ;; Activation gexp for BitlBee. | |
914 | #~(begin | |
915 | (use-modules (guix build utils)) | |
916 | ||
917 | ;; This directory is used to store OTR data. | |
918 | (mkdir-p "/var/lib/bitlbee") | |
919 | (let ((user (getpwnam "bitlbee"))) | |
920 | (chown "/var/lib/bitlbee" | |
921 | (passwd:uid user) (passwd:gid user))))) | |
922 | ||
923 | (define bitlbee-service-type | |
924 | (service-type (name 'bitlbee) | |
925 | (extensions | |
926 | (list (service-extension shepherd-root-service-type | |
927 | bitlbee-shepherd-service) | |
928 | (service-extension account-service-type | |
929 | (const %bitlbee-accounts)) | |
930 | (service-extension activation-service-type | |
931 | (const %bitlbee-activation)))) | |
932 | (default-value (bitlbee-configuration)) | |
933 | (description | |
934 | "Run @url{http://bitlbee.org,BitlBee}, a daemon that acts as | |
935 | a gateway between IRC and chat networks."))) | |
936 | ||
dcad57d5 EF |
937 | \f |
938 | ;;; | |
939 | ;;; Quassel. | |
940 | ;;; | |
941 | ||
942 | (define-record-type* <quassel-configuration> | |
943 | quassel-configuration make-quassel-configuration | |
944 | quassel-configuration? | |
945 | (quassel quassel-configuration-quassel | |
946 | (default quassel)) | |
947 | (interface quassel-configuration-interface | |
948 | (default "::,0.0.0.0")) | |
949 | (port quassel-configuration-port | |
950 | (default 4242)) | |
951 | (loglevel quassel-configuration-loglevel | |
952 | (default "Info"))) | |
953 | ||
954 | (define quassel-shepherd-service | |
955 | (match-lambda | |
956 | (($ <quassel-configuration> quassel interface port loglevel) | |
dac4efc4 LC |
957 | (let ((quassel (least-authority-wrapper |
958 | (file-append quassel "/bin/quasselcore") | |
959 | #:name "quasselcore" | |
960 | #:mappings (list (file-system-mapping | |
961 | (source "/var/lib/quassel") | |
962 | (target source) | |
963 | (writable? #t)) | |
964 | (file-system-mapping | |
965 | (source "/var/log/quassel") | |
966 | (target source) | |
967 | (writable? #t))) | |
968 | ;; XXX: The daemon needs to live in the main user | |
969 | ;; namespace, as root, so it can access /var/lib/quassel | |
970 | ;; owned by "quasselcore". | |
971 | #:namespaces (fold delq %namespaces '(net user))))) | |
dcad57d5 EF |
972 | (list (shepherd-service |
973 | (provision '(quassel)) | |
974 | (requirement '(user-processes networking)) | |
dac4efc4 LC |
975 | (start #~(make-forkexec-constructor |
976 | (list #$quassel | |
977 | "--configdir=/var/lib/quassel" | |
978 | "--logfile=/var/log/quassel/core.log" | |
979 | (string-append "--loglevel=" #$loglevel) | |
980 | (string-append "--port=" (number->string #$port)) | |
981 | (string-append "--listen=" #$interface)))) | |
dcad57d5 EF |
982 | (stop #~(make-kill-destructor)))))))) |
983 | ||
984 | (define %quassel-account | |
985 | (list (user-group (name "quassel") (system? #t)) | |
986 | (user-account | |
987 | (name "quasselcore") | |
988 | (group "quassel") | |
989 | (system? #t) | |
990 | (comment "Quassel daemon user") | |
991 | (home-directory "/var/lib/quassel") | |
992 | (shell (file-append shadow "/sbin/nologin"))))) | |
993 | ||
994 | (define %quassel-activation | |
995 | #~(begin | |
996 | (use-modules (guix build utils)) | |
997 | (mkdir-p "/var/lib/quassel") | |
998 | (mkdir-p "/var/log/quassel") | |
999 | (let ((cert "/var/lib/quassel/quasselCert.pem")) | |
1000 | (unless (file-exists? cert) | |
1001 | (invoke #$(file-append openssl "/bin/openssl") | |
1002 | "req" "-x509" "-nodes" "-batch" "-days" "680" "-newkey" | |
1003 | "rsa" "-keyout" cert "-out" cert))))) | |
1004 | ||
1005 | (define quassel-service-type | |
1006 | (service-type (name 'quassel) | |
1007 | (extensions | |
1008 | (list (service-extension shepherd-root-service-type | |
1009 | quassel-shepherd-service) | |
1010 | (service-extension profile-service-type | |
1011 | (compose list quassel-configuration-quassel)) | |
1012 | (service-extension account-service-type | |
1013 | (const %quassel-account)) | |
1014 | (service-extension activation-service-type | |
1015 | (const %quassel-activation)))) | |
1016 | (default-value (quassel-configuration)) | |
1017 | (description | |
1018 | "Run @url{https://quassel-irc.org/,quasselcore}, the backend | |
1019 | for the distributed IRC client quassel, which allows you to connect from | |
1020 | multiple machines simultaneously."))) |