8 cfg = config.services.mautrix-signal;
9 dataDir = "/var/lib/mautrix-signal";
10 registrationFile = "${dataDir}/signal-registration.yaml";
11 settingsFile = "${dataDir}/config.yaml";
12 settingsFileUnsubstituted = settingsFormat.generate "mautrix-signal-config-unsubstituted.json" cfg.settings;
13 settingsFormat = pkgs.formats.json { };
14 appservicePort = 29328;
16 # to be used with a list of lib.mkIf values
17 optOneOf = lib.lists.findFirst (value: value.condition) (lib.mkIf false null);
18 mkDefaults = lib.mapAttrsRecursive (n: v: lib.mkDefault v);
21 displayname_template = "{{or .ProfileName .PhoneNumber \"Unknown user\"}}";
24 command_prefix = "!signal";
26 permissions."*" = "relay";
30 uri = "file:${dataDir}/mautrix-signal.db?_txlock=immediate";
32 homeserver.address = "http://localhost:8448";
35 port = appservicePort;
38 username = "signalbot";
39 displayname = "Signal Bridge Bot";
43 username_template = "signal_{{.}}";
49 # By default, the following keys/secrets are set to `generate`. This would break when the service
50 # is restarted, since the previously generated configuration will be overwritten everytime.
51 # If encryption is enabled, it's recommended to set those keys via `environmentFile`.
52 encryption.pickle_key = "";
53 provisioning.shared_secret = "";
54 public_media.signing_key = "";
55 direct_media.server_key = "";
58 writers = lib.singleton {
60 format = "pretty-colored";
68 options.services.mautrix-signal = {
69 enable = lib.mkEnableOption "mautrix-signal, a Matrix-Signal puppeting bridge";
71 settings = lib.mkOption {
72 apply = lib.recursiveUpdate defaultConfig;
73 type = settingsFormat.type;
74 default = defaultConfig;
76 {file}`config.yaml` configuration as a Nix attribute set.
77 Configuration options should match those described in the example configuration.
78 Get an example configuration by executing `mautrix-signal -c example.yaml --generate-example-config`
79 Secret tokens should be specified using {option}`environmentFile`
80 instead of this world-readable attribute set.
84 private_chat_portal_meta = true;
85 mute_only_on_create = false;
87 "example.com" = "user";
92 uri = "postgresql:///mautrix_signal?host=/run/postgresql";
95 address = "http://[::1]:8008";
96 domain = "my-domain.tld";
100 ephemeral_events = false;
102 matrix.message_status_events = true;
104 shared_secret = "disable";
106 backfill.enabled = true;
111 pickle_key = "$ENCRYPTION_PICKLE_KEY";
116 environmentFile = lib.mkOption {
117 type = lib.types.nullOr lib.types.path;
120 File containing environment variables to be passed to the mautrix-signal service.
121 If an environment variable `MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET` is set,
122 then its value will be used in the configuration file for the option
123 `double_puppet.secrets` without leaking it to the store, using the configured
124 `homeserver.domain` as key.
128 serviceDependencies = lib.mkOption {
129 type = with lib.types; listOf str;
131 (lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit)
132 ++ (lib.optional config.services.matrix-conduit.enable "conduit.service");
133 defaultText = lib.literalExpression ''
134 (optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit)
135 ++ (optional config.services.matrix-conduit.enable "conduit.service")
138 List of systemd units to require and wait for when starting the application service.
142 registerToSynapse = lib.mkOption {
143 type = lib.types.bool;
144 default = config.services.matrix-synapse.enable;
145 defaultText = lib.literalExpression ''
146 config.services.matrix-synapse.enable
149 Whether to add the bridge's app service registration file to
150 `services.matrix-synapse.settings.app_service_config_files`.
155 config = lib.mkIf cfg.enable {
157 users.users.mautrix-signal = {
159 group = "mautrix-signal";
161 description = "Mautrix-Signal bridge user";
164 users.groups.mautrix-signal = { };
166 services.matrix-synapse = lib.mkIf cfg.registerToSynapse {
167 settings.app_service_config_files = [ registrationFile ];
169 systemd.services.matrix-synapse = lib.mkIf cfg.registerToSynapse {
170 serviceConfig.SupplementaryGroups = [ "mautrix-signal" ];
173 # Note: this is defined here to avoid the docs depending on `config`
174 services.mautrix-signal.settings.homeserver = optOneOf (
175 with config.services;
177 (lib.mkIf matrix-synapse.enable (mkDefaults {
178 domain = matrix-synapse.settings.server_name;
180 (lib.mkIf matrix-conduit.enable (mkDefaults {
181 domain = matrix-conduit.settings.global.server_name;
182 address = "http://localhost:${toString matrix-conduit.settings.global.port}";
187 systemd.services.mautrix-signal = {
188 description = "mautrix-signal, a Matrix-Signal puppeting bridge.";
190 wantedBy = [ "multi-user.target" ];
191 wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
192 after = [ "network-online.target" ] ++ cfg.serviceDependencies;
193 # ffmpeg is required for conversion of voice messages
194 path = [ pkgs.ffmpeg-headless ];
197 # substitute the settings file by environment variables
198 # in this case read from EnvironmentFile
199 test -f '${settingsFile}' && rm -f '${settingsFile}'
202 ${pkgs.envsubst}/bin/envsubst \
203 -o '${settingsFile}' \
204 -i '${settingsFileUnsubstituted}'
207 # generate the appservice's registration file if absent
208 if [ ! -f '${registrationFile}' ]; then
209 ${pkgs.mautrix-signal}/bin/mautrix-signal \
210 --generate-registration \
211 --config='${settingsFile}' \
212 --registration='${registrationFile}'
214 chmod 640 ${registrationFile}
217 # 1. Overwrite registration tokens in config
218 # 2. If environment variable MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET
219 # is set, set it as the login shared secret value for the configured
221 ${pkgs.yq}/bin/yq -s '.[0].appservice.as_token = .[1].as_token
222 | .[0].appservice.hs_token = .[1].hs_token
224 | if env.MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET then .double_puppet.secrets.[.homeserver.domain] = env.MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET else . end' \
225 '${settingsFile}' '${registrationFile}' > '${settingsFile}.tmp'
226 mv '${settingsFile}.tmp' '${settingsFile}'
231 User = "mautrix-signal";
232 Group = "mautrix-signal";
233 EnvironmentFile = cfg.environmentFile;
234 StateDirectory = baseNameOf dataDir;
235 WorkingDirectory = dataDir;
237 ${pkgs.mautrix-signal}/bin/mautrix-signal \
238 --config='${settingsFile}' \
239 --registration='${registrationFile}'
241 LockPersonality = true;
242 MemoryDenyWriteExecute = true;
243 NoNewPrivileges = true;
244 PrivateDevices = true;
248 ProtectControlGroups = true;
250 ProtectHostname = true;
251 ProtectKernelLogs = true;
252 ProtectKernelModules = true;
253 ProtectKernelTunables = true;
254 ProtectSystem = "strict";
255 Restart = "on-failure";
257 RestrictRealtime = true;
258 RestrictSUIDSGID = true;
259 SystemCallArchitectures = "native";
260 SystemCallErrorNumber = "EPERM";
261 SystemCallFilter = [ "@system-service" ];
265 restartTriggers = [ settingsFileUnsubstituted ];
269 buildDocsInSandbox = false;
270 doc = ./mautrix-signal.md;
271 maintainers = with lib.maintainers; [