···22222323- [Broadcast Box](https://github.com/Glimesh/broadcast-box), a WebRTC broadcast server. Available as [services.broadcast-box](options.html#opt-services.broadcast-box.enable).
24242525+- [Draupnir](https://github.com/the-draupnir-project/draupnir), a Matrix moderation bot. Available as [services.draupnir](#opt-services.draupnir.enable).
2626+2527- [SuiteNumérique Docs](https://github.com/suitenumerique/docs), a collaborative note taking, wiki and documentation web platform and alternative to Notion or Outline. Available as [services.lasuite-docs](#opt-services.lasuite-docs.enable).
26282729[dwl](https://codeberg.org/dwl/dwl), a compact, hackable compositor for Wayland based on wlroots. Available as [programs.dwl](#opt-programs.dwl.enable).
···11+# Draupnir (Matrix Moderation Bot) {#module-services-draupnir}
22+33+This chapter will show you how to set up your own, self-hosted
44+[Draupnir](https://github.com/the-draupnir-project/Draupnir) instance.
55+66+As an all-in-one moderation tool, it can protect your server from
77+malicious invites, spam messages, and whatever else you don't want.
88+In addition to server-level protection, Draupnir is great for communities
99+wanting to protect their rooms without having to use their personal
1010+accounts for moderation.
1111+1212+The bot by default includes support for bans, redactions, anti-spam,
1313+server ACLs, room directory changes, room alias transfers, account
1414+deactivation, room shutdown, and more. (This depends on homeserver configuration and implementation.)
1515+1616+See the [README](https://github.com/the-draupnir-project/draupnir#readme)
1717+page and the [Moderator's guide](https://the-draupnir-project.github.io/draupnir-documentation/moderator/setting-up-and-configuring)
1818+for additional instructions on how to setup and use Draupnir.
1919+2020+For [additional settings](#opt-services.draupnir.settings)
2121+see [the default configuration](https://github.com/the-draupnir-project/Draupnir/blob/main/config/default.yaml).
2222+2323+## Draupnir Setup {#module-services-draupnir-setup}
2424+2525+First create a new unencrypted, private room which will be used as the management room for Draupnir.
2626+This is the room in which moderators will interact with Draupnir and where it will log possible errors and debugging information.
2727+You'll need to set this room ID or alias in [services.draupnir.settings.managementRoom](#opt-services.draupnir.settings.managementRoom).
2828+2929+Next, create a new user for Draupnir on your homeserver, if one does not already exist.
3030+3131+The Draupnir Matrix user expects to be free of any rate limiting.
3232+See [Synapse #6286](https://github.com/matrix-org/synapse/issues/6286)
3333+for an example on how to achieve this.
3434+3535+If you want Draupnir to be able to deactivate users, move room aliases, shut down rooms, etc.
3636+you'll need to make the Draupnir user a Matrix server admin.
3737+3838+Now invite the Draupnir user to the management room.
3939+Draupnir will automatically try to join this room on startup.
4040+4141+```nix
4242+{
4343+ services.draupnir = {
4444+ enable = true;
4545+4646+ settings = {
4747+ homeserverUrl = "https://matrix.org";
4848+ managementRoom = "!yyy:example.org";
4949+ };
5050+5151+ secrets = {
5252+ accessToken = "/path/to/secret/containing/access-token";
5353+ };
5454+ };
5555+}
5656+```
5757+5858+### Element Matrix Services (EMS) {#module-services-draupnir-setup-ems}
5959+6060+If you are using a managed ["Element Matrix Services (EMS)"](https://ems.element.io/)
6161+server, you will need to consent to the terms and conditions. Upon startup, an error
6262+log entry with a URL to the consent page will be generated.
+257
nixos/modules/services/matrix/draupnir.nix
···11+{
22+ config,
33+ options,
44+ lib,
55+ pkgs,
66+ ...
77+}:
88+99+let
1010+ cfg = config.services.draupnir;
1111+ opt = options.services.draupnir;
1212+1313+ format = pkgs.formats.yaml { };
1414+ configFile = format.generate "draupnir.yaml" cfg.settings;
1515+1616+ inherit (lib)
1717+ literalExpression
1818+ mkEnableOption
1919+ mkOption
2020+ mkPackageOption
2121+ mkRemovedOptionModule
2222+ mkRenamedOptionModule
2323+ types
2424+ ;
2525+in
2626+{
2727+ imports = [
2828+ # Removed options for those migrating from the Mjolnir module
2929+ (mkRenamedOptionModule
3030+ [ "services" "draupnir" "dataPath" ]
3131+ [ "services" "draupnir" "settings" "dataPath" ]
3232+ )
3333+ (mkRenamedOptionModule
3434+ [ "services" "draupnir" "homeserverUrl" ]
3535+ [ "services" "draupnir" "settings" "homeserverUrl" ]
3636+ )
3737+ (mkRenamedOptionModule
3838+ [ "services" "draupnir" "managementRoom" ]
3939+ [ "services" "draupnir" "settings" "managementRoom" ]
4040+ )
4141+ (mkRenamedOptionModule
4242+ [ "services" "draupnir" "accessTokenFile" ]
4343+ [ "services" "draupnir" "secrets" "accessToken" ]
4444+ )
4545+ (mkRemovedOptionModule [ "services" "draupnir" "pantalaimon" ] ''
4646+ `services.draupnir.pantalaimon.*` has been removed because it depends on the deprecated and vulnerable
4747+ libolm library for end-to-end encryption and upstream support for Pantalaimon in Draupnir is limited.
4848+ See <https://the-draupnir-project.github.io/draupnir-documentation/bot/encryption> for details.
4949+ If you nontheless require E2EE via Pantalaimon, you can configure `services.pantalaimon-headless.instances`
5050+ yourself and use that with `services.draupnir.settings.pantalaimon` and `services.draupnir.secrets.pantalaimon.password`.
5151+ '')
5252+ ];
5353+5454+ options.services.draupnir = {
5555+ enable = mkEnableOption "Draupnir, a moderations bot for Matrix";
5656+5757+ package = mkPackageOption pkgs "draupnir" { };
5858+5959+ settings = mkOption {
6060+ example = literalExpression ''
6161+ {
6262+ homeserverUrl = "https://matrix.org";
6363+ managementRoom = "#moderators:example.org";
6464+6565+ autojoinOnlyIfManager = true;
6666+ automaticallyRedactForReasons = [ "spam" "advertising" ];
6767+ }
6868+ '';
6969+ description = ''
7070+ Free-form settings written to Draupnir's configuration file.
7171+ See [Draupnir's default configuration](https://github.com/the-draupnir-project/Draupnir/blob/main/config/default.yaml) for available settings.
7272+ '';
7373+ default = { };
7474+ type = types.submodule {
7575+ freeformType = format.type;
7676+ options = {
7777+ homeserverUrl = mkOption {
7878+ type = types.str;
7979+ example = "https://matrix.org";
8080+ description = ''
8181+ Base URL of the Matrix homeserver that provides the Client-Server API.
8282+8383+ ::: {.note}
8484+ When using Pantalaimon, set this to the Pantalaimon URL and
8585+ {option}`${opt.settings}.rawHomeserverUrl` to the public URL.
8686+ :::
8787+ '';
8888+ };
8989+9090+ rawHomeserverUrl = mkOption {
9191+ type = types.str;
9292+ example = "https://matrix.org";
9393+ default = cfg.settings.homeserverUrl;
9494+ defaultText = literalExpression "config.${opt.settings}.homeserverUrl";
9595+ description = ''
9696+ Public base URL of the Matrix homeserver that provides the Client-Server API when using the Draupnir's
9797+ [Report forwarding feature](https://the-draupnir-project.github.io/draupnir-documentation/bot/homeserver-administration#report-forwarding).
9898+9999+ ::: {.warning}
100100+ When using Pantalaimon, do not set this to the Pantalaimon URL!
101101+ :::
102102+ '';
103103+ };
104104+105105+ managementRoom = mkOption {
106106+ type = types.str;
107107+ example = "#moderators:example.org";
108108+ description = ''
109109+ The room ID or alias where moderators can use the bot's functionality.
110110+111111+ The bot has no access controls, so anyone in this room can use the bot - secure this room!
112112+ Do not enable end-to-end encryption for this room, unless set up with Pantalaimon.
113113+114114+ ::: {.warning}
115115+ When using a room alias, make sure the alias used is on the local homeserver!
116116+ This prevents an issue where the control room becomes undefined when the alias can't be resolved.
117117+ :::
118118+ '';
119119+ };
120120+121121+ dataPath = mkOption {
122122+ type = types.path;
123123+ readOnly = true;
124124+ default = "/var/lib/draupnir";
125125+ description = ''
126126+ The path Draupnir will store its state/data in.
127127+128128+ ::: {.warning}
129129+ This option is read-only.
130130+ :::
131131+132132+ ::: {.note}
133133+ If you want to customize where this data is stored, use a bind mount.
134134+ :::
135135+ '';
136136+ };
137137+ };
138138+ };
139139+ };
140140+141141+ secrets = {
142142+ accessToken = mkOption {
143143+ type = types.nullOr types.path;
144144+ default = null;
145145+ description = ''
146146+ File containing the access token for Draupnir's Matrix account
147147+ to be used in place of {option}`${opt.settings}.accessToken`.
148148+ '';
149149+ };
150150+151151+ pantalaimon.password = mkOption {
152152+ type = types.nullOr types.path;
153153+ default = null;
154154+ description = ''
155155+ File containing the password for Draupnir's Matrix account when used in
156156+ conjunction with Pantalaimon to be used in place of
157157+ {option}`${opt.settings}.pantalaimon.password`.
158158+159159+ ::: {.warning}
160160+ Take note that upstream has limited Pantalaimon and E2EE support:
161161+ <https://the-draupnir-project.github.io/draupnir-documentation/bot/encryption> and
162162+ <https://the-draupnir-project.github.io/draupnir-documentation/shared/dogfood#e2ee-support>.
163163+ :::
164164+ '';
165165+ };
166166+167167+ web.synapseHTTPAntispam.authorization = mkOption {
168168+ type = types.nullOr types.path;
169169+ default = null;
170170+ description = ''
171171+ File containing the secret token when using the Synapse HTTP Antispam module
172172+ to be used in place of
173173+ {option}`${opt.settings}.web.synapseHTTPAntispam.authorization`.
174174+175175+ See <https://the-draupnir-project.github.io/draupnir-documentation/bot/synapse-http-antispam> for details.
176176+ '';
177177+ };
178178+ };
179179+ };
180180+181181+ config = lib.mkIf cfg.enable {
182182+ assertions = [
183183+ {
184184+ # Removed option for those migrating from the Mjolnir module - mkRemovedOption module does *not* work with submodules.
185185+ assertion = !(cfg.settings ? protectedRooms);
186186+ message = "Unset ${opt.settings}.protectedRooms, as it is unsupported on Draupnir. Add these rooms via `!draupnir rooms add` instead.";
187187+ }
188188+ ];
189189+190190+ systemd.services.draupnir = {
191191+ description = "Draupnir - a moderation bot for Matrix";
192192+ wants = [
193193+ "network-online.target"
194194+ "matrix-synapse.service"
195195+ "conduit.service"
196196+ "dendrite.service"
197197+ ];
198198+ after = [
199199+ "network-online.target"
200200+ "matrix-synapse.service"
201201+ "conduit.service"
202202+ "dendrite.service"
203203+ ];
204204+ wantedBy = [ "multi-user.target" ];
205205+206206+ startLimitIntervalSec = 0;
207207+ serviceConfig = {
208208+ ExecStart = toString (
209209+ [
210210+ (lib.getExe cfg.package)
211211+ "--draupnir-config"
212212+ configFile
213213+ ]
214214+ ++ lib.optionals (cfg.secrets.accessToken != null) [
215215+ "--access-token-path"
216216+ "%d/access_token"
217217+ ]
218218+ ++ lib.optionals (cfg.secrets.pantalaimon.password != null) [
219219+ "--pantalaimon-password-path"
220220+ "%d/pantalaimon_password"
221221+ ]
222222+ ++ lib.optionals (cfg.secrets.web.synapseHTTPAntispam.authorization != null) [
223223+ "--http-antispam-authorization-path"
224224+ "%d/http_antispam_authorization"
225225+ ]
226226+ );
227227+228228+ WorkingDirectory = "/var/lib/draupnir";
229229+ StateDirectory = "draupnir";
230230+ StateDirectoryMode = "0700";
231231+ ProtectHome = true;
232232+ PrivateDevices = true;
233233+ Restart = "on-failure";
234234+ RestartSec = "5s";
235235+ DynamicUser = true;
236236+ LoadCredential =
237237+ lib.optionals (cfg.secrets.accessToken != null) [
238238+ "access_token:${cfg.secrets.accessToken}"
239239+ ]
240240+ ++ lib.optionals (cfg.secrets.pantalaimon.password != null) [
241241+ "pantalaimon_password:${cfg.secrets.pantalaimon.password}"
242242+ ]
243243+ ++ lib.optionals (cfg.secrets.web.synapseHTTPAntispam.authorization != null) [
244244+ "http_antispam_authorization:${cfg.secrets.web.synapseHTTPAntispam.authorization}"
245245+ ];
246246+ };
247247+ };
248248+ };
249249+250250+ meta = {
251251+ doc = ./draupnir.md;
252252+ maintainers = with lib.maintainers; [
253253+ RorySys
254254+ emilylange
255255+ ];
256256+ };
257257+}
···1522915229 lua = lua5_1;
1523015230 };
15231152311523215232- # solarus and solarus-quest-editor must use the same version of Qt.
1523315233- solarus = libsForQt5.callPackage ../games/solarus { };
1523415234- solarus-quest-editor = libsForQt5.callPackage ../development/tools/solarus-quest-editor { };
1523515235-1523615232 # You still can override by passing more arguments.
1523715233 spring = callPackage ../games/spring { asciidoc = asciidoc-full; };
1523815234