diff --git a/options/mailman3/options.nix b/options/mailman3/options.nix
new file mode 100644
index 0000000..570bb42
--- /dev/null
+++ b/options/mailman3/options.nix
@@ -0,0 +1,421 @@
+{ pkgs, lib, config, ... }:
+
+with lib;
+
+let
+ mailman3 = import ./release.nix { };
+ cfg = config.services.mailman3;
+ usePostgresql = cfg.database.type == "postgresql";
+ useSqlite = cfg.database.type == "sqlite3";
+ useMysql = cfg.database.type == "mysql";
+ usePostfix = cfg.mta.type == "postfix";
+ configFile_postfix = pkgs.writeText "postfix.cfg" ''
+ [postfix]
+ transport_file_type: hash
+ postmap_command: ${pkgs.postfix}/bin/postmap
+ '';
+ configFile = pkgs.writeText "mailman.cfg" ''
+ # This File was automatically generated by the mailman3 nixos submodule.
+ # Do not manually edit this file.
+
+ [mailman]
+ site_owner: ${cfg.site_owner}
+ layout: custom
+
+ [paths.custom]
+ archive_dir: ${cfg.paths.archive_dir}
+ bin_dir: ${cfg.paths.bin_dir}
+ cache_dir: ${cfg.paths.cache_dir}
+ data_dir: ${cfg.paths.data_dir}
+ etc_dir: ${cfg.paths.etc_dir}
+ lock_dir: ${cfg.paths.lock_dir}
+ log_dir: ${cfg.paths.log_dir}
+ messages_dir: ${cfg.paths.messages_dir}
+ template_dir: ${cfg.paths.template_dir}
+ var_dir: ${cfg.paths.var_dir}
+ lock_file: ${cfg.paths.lock_file}
+ pid_file: ${cfg.paths.pid_file}
+
+ # The Database Documentation can be found here:
+ # https://mailman.readthedocs.io/en/latest/src/mailman/docs/database.html
+ # https://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls
+ [database]
+ ${optionalString useSqlite ''
+ [database]
+ url: sqlite:///${cfg.database.socket}
+ ''}
+ ${optionalString usePostgresql ''
+ class: mailman.database.postgresql.PostgreSQLDatabase
+ url: postgres://${cfg.database.user}:#dbpass#@${database.host}:${toString cfg.database.port}/${cfg.database.name}
+ ''}
+ ${optionalString useMysql ''
+ class: mailman.database.mysql.MySQLDatabase
+ url: mysql+pymysql://${cfg.database.user}:#dbpass#@${cfg.database.host}:${toString cfg.database.port}/${cfg.database.name}?charset=utf8&use_unicode=1
+ ''}
+
+ [mta]
+ incoming: ${cfg.mta.incoming}
+ outgoing: ${cfg.mta.outgoing}
+ lmtp_host: ${cfg.mta.lmtp_host}
+ lmtp_port: ${toString cfg.mta.lmtp_port}
+ smtp_host: ${cfg.mta.smtp_host}
+ smtp_port: ${toString cfg.mta.smtp_port}
+ smtp_user: ${cfg.mta.smtp_user}
+ smtp_pass: #smtppass#
+ # One of smtp/smtps/starttls, specifies the protocol Mailman will use when
+ # connecting. Typically will correspond to smtp_port: 25 -> smtp, 465 -> smtps,
+ # 587 -> starttls.
+ smtp_secure_mode: smtp
+ configuration: ${configFile_postfix}
+
+ [ARC]
+ enabled: no
+
+ ${cfg.extraConfig}
+ '';
+in {
+ options.services.mailman3 = {
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ example = true;
+ description = ''
+ Wether to enable mailman3 mailing list management system (core).
+ '';
+ };
+
+ site_owner = mkOption {
+ type = types.str;
+ default = "changeme@example.com";
+ description = ''
+ mailman3 site owner address.
+ '';
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "mailman3";
+ description = ''
+ User account under which mailman runs.
+ '';
+ };
+
+ database = {
+ type = mkOption {
+ type = types.enum [ "sqlite3" "mysql" "postgres" ];
+ default = "sqlite3";
+ example = "mysql";
+ description = ''
+ Database type to use.
+ '';
+ };
+
+ host = mkOption {
+ type = types.str;
+ default = "172.0.0.1";
+ description = ''
+ Database host adress.
+ '';
+ };
+
+ port = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ description = ''
+
+ '';
+ };
+
+ name = mkOption {
+ type = types.str;
+ default = "mailman3";
+ description = ''
+ Database name.
+ '';
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "mailman3";
+ description = ''
+ Database user.
+ '';
+ };
+
+ password = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+ The password corresponding to .
+ Warning: this is stored in cleartext in the Nix store!
+ Use instead.
+ '';
+ };
+
+ passwordFile = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/run/keys/mailman3-dbpassword";
+ description = ''
+ A file containing the password corresponding to
+ .
+ '';
+ };
+
+ socket = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ example = "/run/mysqld/mysqld.sock";
+ description = "Path to the unix socket file to use for authentication.";
+ };
+ };
+
+ paths = {
+ archive_dir = mkOption {
+ type = types.str;
+ default = "${cfg.paths.var_dir}/archives";
+ description = ''
+
+ '';
+ };
+
+ bin_dir = mkOption {
+ type = types.str;
+ default = "${mailman3.core}/bin";
+ description = ''
+
+ '';
+ };
+
+ cache_dir = mkOption {
+ type = types.str;
+ default = "${cfg.paths.var_dir}/cache";
+ description = ''
+
+ '';
+ };
+
+ data_dir = mkOption {
+ type = types.str;
+ default = "${cfg.paths.var_dir}/data";
+ description = ''
+
+ '';
+ };
+
+ etc_dir = mkOption {
+ type = types.str;
+ default = "${cfg.paths.var_dir}/etc";
+ description = ''
+
+ '';
+ };
+
+ lock_dir = mkOption {
+ type = types.str;
+ default = "${cfg.paths.var_dir}/locks";
+ description = ''
+
+ '';
+ };
+
+ log_dir = mkOption {
+ type = types.str;
+ default = "${cfg.paths.var_dir}/logs";
+ description = ''
+
+ '';
+ };
+
+ messages_dir = mkOption {
+ type = types.str;
+ default = "${cfg.paths.var_dir}/messages";
+ description = ''
+
+ '';
+ };
+
+ template_dir = mkOption {
+ type = types.str;
+ default = "${cfg.paths.var_dir}/templates";
+ description = ''
+
+ '';
+ };
+
+ var_dir = mkOption {
+ type = types.str;
+ default = "/var/lib/mailman3";
+ description = ''
+
+ '';
+ };
+
+ lock_file = mkOption {
+ type = types.path;
+ default = "${cfg.paths.lock_dir}/master.lck";
+ description = ''
+
+ '';
+ };
+
+ pid_file = mkOption {
+ type = types.path;
+ default = "${cfg.paths.var_dir}/master.pid";
+ description = ''
+
+ '';
+ };
+ };
+
+ mta = {
+ # TODO: add Sentmail and qmail
+ type = mkOption {
+ type = types.enum [ "postfix" "exim4" ];
+ default = "postfix";
+ example = "exim4";
+ description = ''
+ Database type to use.
+ '';
+ };
+ incoming = mkOption {
+ type = types.str;
+ default = "mailman.mta.${cfg.mta.type}.LMTP";
+ description = ''
+
+ '';
+ };
+ outgoing = mkOption {
+ type = types.str;
+ default = "mailman.mta.deliver.deliver";
+ description = ''
+
+ '';
+ };
+ lmtp_host = mkOption {
+ type = types.str;
+ default = "mail.example.com";
+ description = ''
+
+ '';
+ };
+ lmtp_port = mkOption {
+ type = types.int;
+ default = 8024;
+ description = ''
+
+ '';
+ };
+ smtp_host = mkOption {
+ type = types.str;
+ default = "mail.example.com";
+ description = ''
+
+ '';
+ };
+ smtp_port = mkOption {
+ type = types.int;
+ default = 25;
+ description = ''
+
+ '';
+ };
+ smtp_user = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+
+ '';
+ };
+ smtp_pass = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+
+ '';
+ };
+ smtp_passFile = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+
+ '';
+ };
+ };
+
+ extraConfig = mkOption {
+ type = types.str;
+ default = "";
+ example = ''
+ [devmode]
+ enabled: yes
+ recipient: your.address@your.domain
+ '';
+ description = ''
+ Configuration lines appended to the generated mailman3 configuration file.
+ '';
+ };
+ };
+
+
+ config = mkIf cfg.enable {
+ users = mkIf (cfg.user == "mailman3") {
+ users.mailman3 = {
+ description = "Mailman3 Service";
+ home = cfg.paths.var_dir;
+ createHome = true;
+ useDefaultShell = true;
+ packages = [ mailman3.core ];
+ };
+ };
+
+ services.postfix.recipientDelimiter = mkIf usePostfix (mkDefault "+");
+ services.postfix.mapFiles."transport_maps" = mkIf usePostfix (mkDefault "${cfg.paths.data_dir}/postfix_lmtp");
+ services.postfix.mapFiles."local_recipient_maps" = mkIf usePostfix (mkDefault "${cfg.paths.data_dir}/postfix_lmtp");
+ services.postfix.mapFiles."relay_domains" = mkIf usePostfix (mkDefault "${cfg.paths.data_dir}/postfix_domains");
+
+ warnings = optional (cfg.database.password != "")
+ ''config.services.mailman3.database.password will be stored as plaintext
+ in the Nix store. Use database.passwordFile instead.'';
+
+ # Create database passwordFile default when password is configured.
+ services.mailman3.database.passwordFile =
+ (mkDefault (toString (pkgs.writeTextFile {
+ name = "mailman3-database-password";
+ text = cfg.database.password;
+ })));
+
+ systemd.services.mailman3 = {
+ description = "GNU Mailing List Manager";
+ after = [ "network.target" ] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service";
+ wantedBy = [ "multi-user.target" ];
+
+ preStart = let
+ dbpass = (fileContents cfg.database.passwordFile);
+ smtppass = (fileContents cfg.mta.smtp_passFile);
+ in ''
+ mkdir -p ${cfg.paths.etc_dir}
+ cp ${configFile} ${cfg.paths.etc_dir}/mailman.cfg
+ ${optionalString (useMysql || usePostgresql) ''
+ sed -e "s/#dbpass#/${dbpass}/g" -e "s/#smtppass#/${smtppass}/g" -i ${cfg.paths.etc_dir}/mailman.cfg
+ ''}
+ chmod 640 ${cfg.paths.etc_dir}/mailman.cfg
+ '';
+
+ serviceConfig = {
+ Type = "forking";
+ User = cfg.user;
+ WorkingDirectory = cfg.paths.var_dir;
+ ExecStart = "${mailman3.core}/bin/mailman start -f";
+ ExecReload = "${mailman3.core}/bin/mailman restart";
+ ExecStop = "${mailman3.core}/bin/mailman stop";
+ };
+
+ environment = {
+ USER = cfg.user;
+ HOME = cfg.paths.var_dir;
+ MAILMAN_CONFIG_FILE = "${cfg.paths.etc_dir}/mailman.cfg";
+ };
+ };
+ };
+}