1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10let
11 cfg = config.services.jitsi-meet;
12
13 # The configuration files are JS of format "var <<string>> = <<JSON>>;". In order to
14 # override only some settings, we need to extract the JSON, use jq to merge it with
15 # the config provided by user, and then reconstruct the file.
16 overrideJs =
17 source: varName: userCfg: appendExtra:
18 let
19 extractor = pkgs.writeText "extractor.js" ''
20 var fs = require("fs");
21 eval(fs.readFileSync(process.argv[2], 'utf8'));
22 process.stdout.write(JSON.stringify(eval(process.argv[3])));
23 '';
24 userJson = pkgs.writeText "user.json" (builtins.toJSON userCfg);
25 in
26 (pkgs.runCommand "${varName}.js" { } ''
27 ${pkgs.nodejs}/bin/node ${extractor} ${source} ${varName} > default.json
28 (
29 echo "var ${varName} = "
30 ${pkgs.jq}/bin/jq -s '.[0] * .[1]' default.json ${userJson}
31 echo ";"
32 echo ${escapeShellArg appendExtra}
33 ) > $out
34 '');
35
36 # Essential config - it's probably not good to have these as option default because
37 # types.attrs doesn't do merging. Let's merge explicitly, can still be overridden if
38 # user desires.
39 defaultCfg = {
40 hosts = {
41 domain = cfg.hostName;
42 muc = "conference.${cfg.hostName}";
43 focus = "focus.${cfg.hostName}";
44 jigasi = "jigasi.${cfg.hostName}";
45 };
46 bosh = "//${cfg.hostName}/http-bind";
47 websocket = "wss://${cfg.hostName}/xmpp-websocket";
48
49 fileRecordingsEnabled = true;
50 liveStreamingEnabled = true;
51 hiddenDomain = "recorder.${cfg.hostName}";
52 };
53in
54{
55 options.services.jitsi-meet = with types; {
56 enable = mkEnableOption "Jitsi Meet - Secure, Simple and Scalable Video Conferences";
57
58 hostName = mkOption {
59 type = str;
60 example = "meet.example.org";
61 description = ''
62 FQDN of the Jitsi Meet instance.
63 '';
64 };
65
66 config = mkOption {
67 type = attrs;
68 default = { };
69 example = literalExpression ''
70 {
71 enableWelcomePage = false;
72 defaultLang = "fi";
73 }
74 '';
75 description = ''
76 Client-side web application settings that override the defaults in {file}`config.js`.
77
78 See <https://github.com/jitsi/jitsi-meet/blob/master/config.js> for default
79 configuration with comments.
80 '';
81 };
82
83 extraConfig = mkOption {
84 type = lines;
85 default = "";
86 description = ''
87 Text to append to {file}`config.js` web application config file.
88
89 Can be used to insert JavaScript logic to determine user's region in cascading bridges setup.
90 '';
91 };
92
93 interfaceConfig = mkOption {
94 type = attrs;
95 default = { };
96 example = literalExpression ''
97 {
98 SHOW_JITSI_WATERMARK = false;
99 SHOW_WATERMARK_FOR_GUESTS = false;
100 }
101 '';
102 description = ''
103 Client-side web-app interface settings that override the defaults in {file}`interface_config.js`.
104
105 See <https://github.com/jitsi/jitsi-meet/blob/master/interface_config.js> for
106 default configuration with comments.
107 '';
108 };
109
110 videobridge = {
111 enable = mkOption {
112 type = bool;
113 default = true;
114 description = ''
115 Jitsi Videobridge instance and configure it to connect to Prosody.
116
117 Additional configuration is possible with {option}`services.jitsi-videobridge`
118 '';
119 };
120
121 passwordFile = mkOption {
122 type = nullOr str;
123 default = null;
124 example = "/run/keys/videobridge";
125 description = ''
126 File containing password to the Prosody account for videobridge.
127
128 If `null`, a file with password will be generated automatically. Setting
129 this option is useful if you plan to connect additional videobridges to the XMPP server.
130 '';
131 };
132 };
133
134 jicofo.enable = mkOption {
135 type = bool;
136 default = true;
137 description = ''
138 Whether to enable JiCoFo instance and configure it to connect to Prosody.
139
140 Additional configuration is possible with {option}`services.jicofo`.
141 '';
142 };
143
144 jibri.enable = mkOption {
145 type = bool;
146 default = false;
147 description = ''
148 Whether to enable a Jibri instance and configure it to connect to Prosody.
149
150 Additional configuration is possible with {option}`services.jibri`, and
151 {option}`services.jibri.finalizeScript` is especially useful.
152 '';
153 };
154
155 jigasi.enable = mkOption {
156 type = bool;
157 default = false;
158 description = ''
159 Whether to enable jigasi instance and configure it to connect to Prosody.
160
161 Additional configuration is possible with <option>services.jigasi</option>.
162 '';
163 };
164
165 nginx.enable = mkOption {
166 type = bool;
167 default = true;
168 description = ''
169 Whether to enable nginx virtual host that will serve the javascript application and act as
170 a proxy for the XMPP server. Further nginx configuration can be done by adapting
171 {option}`services.nginx.virtualHosts.<hostName>`.
172 When this is enabled, ACME will be used to retrieve a TLS certificate by default. To disable
173 this, set the {option}`services.nginx.virtualHosts.<hostName>.enableACME` to
174 `false` and if appropriate do the same for
175 {option}`services.nginx.virtualHosts.<hostName>.forceSSL`.
176 '';
177 };
178
179 caddy.enable = mkEnableOption "caddy reverse proxy to expose jitsi-meet";
180
181 prosody.enable = mkOption {
182 type = bool;
183 default = true;
184 example = false;
185 description = ''
186 Whether to configure Prosody to relay XMPP messages between Jitsi Meet components. Turn this
187 off if you want to configure it manually.
188 '';
189 };
190
191 prosody.allowners_muc = mkOption {
192 type = bool;
193 default = false;
194 description = ''
195 Add module allowners, any user in chat is able to
196 kick other. Usefull in jitsi-meet to kick ghosts.
197 '';
198 };
199
200 prosody.lockdown = mkOption {
201 type = bool;
202 default = false;
203 example = true;
204 description = ''
205 Whether to disable Prosody features not needed by Jitsi Meet.
206
207 The default Prosody configuration assumes that it will be used as a
208 general-purpose XMPP server rather than as a companion service for
209 Jitsi Meet. This option reconfigures Prosody to only listen on
210 localhost without support for TLS termination, XMPP federation or
211 the file transfer proxy.
212 '';
213 };
214
215 excalidraw.enable = mkEnableOption "Excalidraw collaboration backend for Jitsi";
216 excalidraw.port = mkOption {
217 type = types.port;
218 default = 3002;
219 description = ''The port which the Excalidraw backend for Jitsi should listen to.'';
220 };
221
222 secureDomain = {
223 enable = mkEnableOption "Authenticated room creation";
224 authentication = mkOption {
225 type = types.str;
226 default = "internal_hashed";
227 description = ''The authentication type to be used by jitsi'';
228 };
229 };
230 };
231
232 config = mkIf cfg.enable {
233 services.prosody = mkIf cfg.prosody.enable {
234 enable = mkDefault true;
235 xmppComplianceSuite = mkDefault false;
236 modules = {
237 admin_adhoc = mkDefault false;
238 bosh = mkDefault true;
239 ping = mkDefault true;
240 roster = mkDefault true;
241 saslauth = mkDefault true;
242 smacks = mkDefault true;
243 tls = mkDefault true;
244 websocket = mkDefault true;
245 proxy65 = mkIf cfg.prosody.lockdown (mkDefault false);
246 };
247 httpInterfaces = mkIf cfg.prosody.lockdown (mkDefault [ "127.0.0.1" ]);
248 httpsPorts = mkIf cfg.prosody.lockdown (mkDefault [ ]);
249 muc = [
250 {
251 domain = "conference.${cfg.hostName}";
252 name = "Jitsi Meet MUC";
253 allowners_muc = cfg.prosody.allowners_muc;
254 roomLocking = false;
255 roomDefaultPublicJids = true;
256 extraConfig = ''
257 restrict_room_creation = true
258 storage = "memory"
259 admins = { "focus@auth.${cfg.hostName}" }
260 '';
261 }
262 {
263 domain = "breakout.${cfg.hostName}";
264 name = "Jitsi Meet Breakout MUC";
265 roomLocking = false;
266 roomDefaultPublicJids = true;
267 extraConfig = ''
268 restrict_room_creation = true
269 storage = "memory"
270 admins = { "focus@auth.${cfg.hostName}", "jvb@auth.${cfg.hostName}" }
271 '';
272 }
273 {
274 domain = "internal.auth.${cfg.hostName}";
275 name = "Jitsi Meet Videobridge MUC";
276 roomLocking = false;
277 roomDefaultPublicJids = true;
278 extraConfig = ''
279 storage = "memory"
280 admins = { "focus@auth.${cfg.hostName}", "jvb@auth.${cfg.hostName}", "jigasi@auth.${cfg.hostName}" }
281 '';
282 #-- muc_room_cache_size = 1000
283 }
284 {
285 domain = "lobby.${cfg.hostName}";
286 name = "Jitsi Meet Lobby MUC";
287 roomLocking = false;
288 roomDefaultPublicJids = true;
289 extraConfig = ''
290 restrict_room_creation = true
291 storage = "memory"
292 '';
293 }
294 ];
295 extraModules = [
296 "pubsub"
297 "smacks"
298 "speakerstats"
299 "external_services"
300 "conference_duration"
301 "end_conference"
302 "muc_lobby_rooms"
303 "muc_breakout_rooms"
304 "av_moderation"
305 "muc_hide_all"
306 "muc_meeting_id"
307 "muc_domain_mapper"
308 "muc_rate_limit"
309 "limits_exception"
310 "persistent_lobby"
311 "room_metadata"
312 ];
313 extraPluginPaths = [ "${pkgs.jitsi-meet-prosody}/share/prosody-plugins" ];
314 extraConfig = lib.mkMerge [
315 (mkAfter ''
316 Component "focus.${cfg.hostName}" "client_proxy"
317 target_address = "focus@auth.${cfg.hostName}"
318
319 Component "jigasi.${cfg.hostName}" "client_proxy"
320 target_address = "jigasi@auth.${cfg.hostName}"
321
322 Component "speakerstats.${cfg.hostName}" "speakerstats_component"
323 muc_component = "conference.${cfg.hostName}"
324
325 Component "conferenceduration.${cfg.hostName}" "conference_duration_component"
326 muc_component = "conference.${cfg.hostName}"
327
328 Component "endconference.${cfg.hostName}" "end_conference"
329 muc_component = "conference.${cfg.hostName}"
330
331 Component "avmoderation.${cfg.hostName}" "av_moderation_component"
332 muc_component = "conference.${cfg.hostName}"
333
334 Component "metadata.${cfg.hostName}" "room_metadata_component"
335 muc_component = "conference.${cfg.hostName}"
336 breakout_rooms_component = "breakout.${cfg.hostName}"
337 '')
338 (mkBefore (
339 ''
340 muc_mapper_domain_base = "${cfg.hostName}"
341
342 cross_domain_websocket = true;
343 consider_websocket_secure = true;
344
345 unlimited_jids = {
346 "focus@auth.${cfg.hostName}",
347 "jvb@auth.${cfg.hostName}"
348 }
349 ''
350 + optionalString cfg.prosody.lockdown ''
351 c2s_interfaces = { "127.0.0.1" };
352 modules_disabled = { "s2s" };
353 ''
354 ))
355 ];
356 virtualHosts.${cfg.hostName} = {
357 enabled = true;
358 domain = cfg.hostName;
359 extraConfig = ''
360 authentication = ${
361 if cfg.secureDomain.enable then "\"${cfg.secureDomain.authentication}\"" else "\"jitsi-anonymous\""
362 }
363 c2s_require_encryption = false
364 admins = { "focus@auth.${cfg.hostName}" }
365 smacks_max_unacked_stanzas = 5
366 smacks_hibernation_time = 60
367 smacks_max_hibernated_sessions = 1
368 smacks_max_old_sessions = 1
369
370 av_moderation_component = "avmoderation.${cfg.hostName}"
371 speakerstats_component = "speakerstats.${cfg.hostName}"
372 conference_duration_component = "conferenceduration.${cfg.hostName}"
373 end_conference_component = "endconference.${cfg.hostName}"
374
375 c2s_require_encryption = false
376 lobby_muc = "lobby.${cfg.hostName}"
377 breakout_rooms_muc = "breakout.${cfg.hostName}"
378 room_metadata_component = "metadata.${cfg.hostName}"
379 main_muc = "conference.${cfg.hostName}"
380 '';
381 ssl = {
382 cert = "/var/lib/jitsi-meet/jitsi-meet.crt";
383 key = "/var/lib/jitsi-meet/jitsi-meet.key";
384 };
385 };
386 virtualHosts."auth.${cfg.hostName}" = {
387 enabled = true;
388 domain = "auth.${cfg.hostName}";
389 extraConfig = ''
390 authentication = "internal_hashed"
391 '';
392 ssl = {
393 cert = "/var/lib/jitsi-meet/jitsi-meet.crt";
394 key = "/var/lib/jitsi-meet/jitsi-meet.key";
395 };
396 };
397 virtualHosts."recorder.${cfg.hostName}" = {
398 enabled = true;
399 domain = "recorder.${cfg.hostName}";
400 extraConfig = ''
401 authentication = "internal_plain"
402 c2s_require_encryption = false
403 '';
404 };
405 virtualHosts."guest.${cfg.hostName}" = {
406 enabled = true;
407 domain = "guest.${cfg.hostName}";
408 extraConfig = ''
409 authentication = "anonymous"
410 c2s_require_encryption = false
411 '';
412 };
413 };
414 systemd.services.prosody = mkIf cfg.prosody.enable {
415 preStart =
416 let
417 videobridgeSecret =
418 if cfg.videobridge.passwordFile != null then
419 cfg.videobridge.passwordFile
420 else
421 "/var/lib/jitsi-meet/videobridge-secret";
422 in
423 ''
424 ${config.services.prosody.package}/bin/prosodyctl register focus auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jicofo-user-secret)"
425 ${config.services.prosody.package}/bin/prosodyctl register jvb auth.${cfg.hostName} "$(cat ${videobridgeSecret})"
426 ${config.services.prosody.package}/bin/prosodyctl mod_roster_command subscribe focus.${cfg.hostName} focus@auth.${cfg.hostName}
427 ${config.services.prosody.package}/bin/prosodyctl register jibri auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-auth-secret)"
428 ${config.services.prosody.package}/bin/prosodyctl register recorder recorder.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-recorder-secret)"
429 ''
430 + optionalString cfg.jigasi.enable ''
431 ${config.services.prosody.package}/bin/prosodyctl register jigasi auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jigasi-user-secret)"
432 '';
433
434 serviceConfig = {
435 EnvironmentFile = [ "/var/lib/jitsi-meet/secrets-env" ];
436 SupplementaryGroups = [ "jitsi-meet" ];
437 };
438 reloadIfChanged = true;
439 };
440
441 users.groups.jitsi-meet = { };
442 systemd.tmpfiles.rules = [
443 "d '/var/lib/jitsi-meet' 0750 root jitsi-meet - -"
444 ];
445
446 systemd.services.jitsi-meet-init-secrets = {
447 wantedBy = [ "multi-user.target" ];
448 before =
449 [
450 "jicofo.service"
451 "jitsi-videobridge2.service"
452 ]
453 ++ (optional cfg.prosody.enable "prosody.service")
454 ++ (optional cfg.jigasi.enable "jigasi.service");
455 serviceConfig = {
456 Type = "oneshot";
457 UMask = "027";
458 User = "root";
459 Group = "jitsi-meet";
460 WorkingDirectory = "/var/lib/jitsi-meet";
461 };
462
463 script =
464 let
465 secrets =
466 [
467 "jicofo-component-secret"
468 "jicofo-user-secret"
469 "jibri-auth-secret"
470 "jibri-recorder-secret"
471 ]
472 ++ (optionals cfg.jigasi.enable [
473 "jigasi-user-secret"
474 "jigasi-component-secret"
475 ])
476 ++ (optional (cfg.videobridge.passwordFile == null) "videobridge-secret");
477 in
478 ''
479 ${concatMapStringsSep "\n" (s: ''
480 if [ ! -f ${s} ]; then
481 tr -dc a-zA-Z0-9 </dev/urandom | head -c 64 > ${s}
482 fi
483 '') secrets}
484
485 # for easy access in prosody
486 echo "JICOFO_COMPONENT_SECRET=$(cat jicofo-component-secret)" > secrets-env
487 echo "JIGASI_COMPONENT_SECRET=$(cat jigasi-component-secret)" >> secrets-env
488 ''
489 + optionalString cfg.prosody.enable ''
490 # generate self-signed certificates
491 if [ ! -f /var/lib/jitsi-meet/jitsi-meet.crt ]; then
492 ${getBin pkgs.openssl}/bin/openssl req \
493 -x509 \
494 -newkey rsa:4096 \
495 -keyout /var/lib/jitsi-meet/jitsi-meet.key \
496 -out /var/lib/jitsi-meet/jitsi-meet.crt \
497 -days 36500 \
498 -nodes \
499 -subj '/CN=${cfg.hostName}/CN=auth.${cfg.hostName}'
500 chmod 640 /var/lib/jitsi-meet/jitsi-meet.key
501 fi
502 '';
503 };
504
505 systemd.services.jitsi-excalidraw = mkIf cfg.excalidraw.enable {
506 description = "Excalidraw collaboration backend for Jitsi";
507 after = [ "network.target" ];
508 wantedBy = [ "multi-user.target" ];
509 environment.PORT = toString cfg.excalidraw.port;
510
511 serviceConfig = {
512 Type = "simple";
513 ExecStart = "${pkgs.jitsi-excalidraw}/bin/jitsi-excalidraw-backend";
514 Restart = "on-failure";
515
516 DynamicUser = true;
517 Group = "jitsi-meet";
518 CapabilityBoundingSet = "";
519 NoNewPrivileges = true;
520 ProtectSystem = "strict";
521 ProtectClock = true;
522 ProtectHome = true;
523 ProtectProc = true;
524 ProtectKernelLogs = true;
525 PrivateTmp = true;
526 PrivateDevices = true;
527 PrivateUsers = true;
528 ProtectHostname = true;
529 ProtectKernelTunables = true;
530 ProtectKernelModules = true;
531 ProtectControlGroups = true;
532 RestrictAddressFamilies = [
533 "AF_INET"
534 "AF_INET6"
535 ];
536 RestrictNamespaces = true;
537 LockPersonality = true;
538 RestrictRealtime = true;
539 RestrictSUIDSGID = true;
540 SystemCallFilter = [
541 "@system-service @pkey"
542 "~@privileged"
543 ];
544 };
545 };
546
547 services.nginx = mkIf cfg.nginx.enable {
548 enable = mkDefault true;
549 virtualHosts.${cfg.hostName} = {
550 enableACME = mkDefault true;
551 forceSSL = mkDefault true;
552 root = pkgs.jitsi-meet;
553 extraConfig = ''
554 ssi on;
555 '';
556 locations."@root_path".extraConfig = ''
557 rewrite ^/(.*)$ / break;
558 '';
559 locations."~ ^/([^/\\?&:'\"]+)$".tryFiles = "$uri @root_path";
560 locations."^~ /xmpp-websocket" = {
561 priority = 100;
562 proxyPass = "http://localhost:5280/xmpp-websocket";
563 proxyWebsockets = true;
564 };
565 locations."=/http-bind" = {
566 proxyPass = "http://localhost:5280/http-bind";
567 extraConfig = ''
568 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
569 proxy_set_header Host $host;
570 '';
571 };
572 locations."=/external_api.js" = mkDefault {
573 alias = "${pkgs.jitsi-meet}/libs/external_api.min.js";
574 };
575 locations."=/_api/room-info" = {
576 proxyPass = "http://localhost:5280/room-info";
577 extraConfig = ''
578 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
579 proxy_set_header Host $host;
580 '';
581 };
582 locations."=/config.js" = mkDefault {
583 alias =
584 overrideJs "${pkgs.jitsi-meet}/config.js" "config" (recursiveUpdate defaultCfg cfg.config)
585 cfg.extraConfig;
586 };
587 locations."=/interface_config.js" = mkDefault {
588 alias =
589 overrideJs "${pkgs.jitsi-meet}/interface_config.js" "interfaceConfig" cfg.interfaceConfig
590 "";
591 };
592 locations."/socket.io/" = mkIf cfg.excalidraw.enable {
593 proxyPass = "http://127.0.0.1:${toString cfg.excalidraw.port}";
594 proxyWebsockets = true;
595 };
596 };
597 };
598
599 services.caddy = mkIf cfg.caddy.enable {
600 enable = mkDefault true;
601 virtualHosts.${cfg.hostName} = {
602 extraConfig =
603 let
604 templatedJitsiMeet = pkgs.runCommand "templated-jitsi-meet" { } ''
605 cp -R --no-preserve=all ${pkgs.jitsi-meet}/* .
606 for file in *.html **/*.html ; do
607 ${pkgs.sd}/bin/sd '<!--#include virtual="(.*)" -->' '{{ include "$1" }}' $file
608 done
609 rm config.js
610 rm interface_config.js
611 cp -R . $out
612 cp ${
613 overrideJs "${pkgs.jitsi-meet}/config.js" "config" (recursiveUpdate defaultCfg cfg.config)
614 cfg.extraConfig
615 } $out/config.js
616 cp ${
617 overrideJs "${pkgs.jitsi-meet}/interface_config.js" "interfaceConfig" cfg.interfaceConfig ""
618 } $out/interface_config.js
619 cp ./libs/external_api.min.js $out/external_api.js
620 '';
621 in
622 (optionalString cfg.excalidraw.enable ''
623 handle /socket.io/ {
624 reverse_proxy 127.0.0.1:${toString cfg.excalidraw.port}
625 }
626 '')
627 + ''
628 handle /http-bind {
629 header Host ${cfg.hostName}
630 reverse_proxy 127.0.0.1:5280
631 }
632 handle /xmpp-websocket {
633 reverse_proxy 127.0.0.1:5280
634 }
635 handle {
636 templates
637 root * ${templatedJitsiMeet}
638 try_files {path} {path}
639 try_files {path} /index.html
640 file_server
641 }
642 '';
643 };
644 };
645
646 services.jitsi-meet.config =
647 recursiveUpdate
648 (mkIf cfg.excalidraw.enable {
649 whiteboard = {
650 enabled = true;
651 collabServerBaseUrl = "https://${cfg.hostName}";
652 };
653 })
654 (
655 mkIf cfg.secureDomain.enable {
656 hosts.anonymousdomain = "guest.${cfg.hostName}";
657 }
658 );
659
660 services.jitsi-videobridge = mkIf cfg.videobridge.enable {
661 enable = true;
662 xmppConfigs."localhost" = {
663 userName = "jvb";
664 domain = "auth.${cfg.hostName}";
665 passwordFile = "/var/lib/jitsi-meet/videobridge-secret";
666 mucJids = "jvbbrewery@internal.auth.${cfg.hostName}";
667 disableCertificateVerification = true;
668 };
669 };
670
671 services.jicofo = mkIf cfg.jicofo.enable {
672 enable = true;
673 xmppHost = "localhost";
674 xmppDomain = cfg.hostName;
675 userDomain = "auth.${cfg.hostName}";
676 userName = "focus";
677 userPasswordFile = "/var/lib/jitsi-meet/jicofo-user-secret";
678 componentPasswordFile = "/var/lib/jitsi-meet/jicofo-component-secret";
679 bridgeMuc = "jvbbrewery@internal.auth.${cfg.hostName}";
680 config = mkMerge [
681 {
682 jicofo.xmpp.service.disable-certificate-verification = true;
683 jicofo.xmpp.client.disable-certificate-verification = true;
684 }
685 (lib.mkIf (config.services.jibri.enable || cfg.jibri.enable) {
686 jicofo.jibri = {
687 brewery-jid = "JibriBrewery@internal.auth.${cfg.hostName}";
688 pending-timeout = "90";
689 };
690 })
691 (lib.mkIf cfg.secureDomain.enable {
692 jicofo = {
693 authentication = {
694 enabled = "true";
695 type = "XMPP";
696 login-url = cfg.hostName;
697 };
698 xmpp.client.client-proxy = "focus.${cfg.hostName}";
699 };
700 })
701 ];
702 };
703
704 services.jibri = mkIf cfg.jibri.enable {
705 enable = true;
706
707 xmppEnvironments."jitsi-meet" = {
708 xmppServerHosts = [ "localhost" ];
709 xmppDomain = cfg.hostName;
710
711 control.muc = {
712 domain = "internal.auth.${cfg.hostName}";
713 roomName = "JibriBrewery";
714 nickname = "jibri";
715 };
716
717 control.login = {
718 domain = "auth.${cfg.hostName}";
719 username = "jibri";
720 passwordFile = "/var/lib/jitsi-meet/jibri-auth-secret";
721 };
722
723 call.login = {
724 domain = "recorder.${cfg.hostName}";
725 username = "recorder";
726 passwordFile = "/var/lib/jitsi-meet/jibri-recorder-secret";
727 };
728
729 usageTimeout = "0";
730 disableCertificateVerification = true;
731 stripFromRoomDomain = "conference.";
732 };
733 };
734
735 services.jigasi = mkIf cfg.jigasi.enable {
736 enable = true;
737 xmppHost = "localhost";
738 xmppDomain = cfg.hostName;
739 userDomain = "auth.${cfg.hostName}";
740 userName = "jigasi";
741 userPasswordFile = "/var/lib/jitsi-meet/jigasi-user-secret";
742 componentPasswordFile = "/var/lib/jitsi-meet/jigasi-component-secret";
743 bridgeMuc = "jigasibrewery@internal.${cfg.hostName}";
744 config = {
745 "org.jitsi.jigasi.ALWAYS_TRUST_MODE_ENABLED" = "true";
746 };
747 };
748 };
749
750 meta.doc = ./jitsi-meet.md;
751 meta.maintainers = lib.teams.jitsi.members;
752}