diff --git a/config/basic/default.nix b/config/basic/default.nix index ae7f99c..6cd2e55 100644 --- a/config/basic/default.nix +++ b/config/basic/default.nix @@ -10,6 +10,7 @@ ./net.nix ./nftables.nix ./nix.nix + ./secrets.nix ./ssh.nix ./system.nix ./users.nix @@ -28,6 +29,7 @@ inputs.agenix.nixosModules.default inputs.disko.nixosModules.disko inputs.home-manager.nixosModules.default + inputs.idmail.nixosModules.default inputs.impermanence.nixosModules.impermanence inputs.lanzaboote.nixosModules.lanzaboote inputs.nix-topology.nixosModules.default diff --git a/config/basic/secrets.nix b/config/basic/secrets.nix new file mode 100644 index 0000000..b7b7bcb --- /dev/null +++ b/config/basic/secrets.nix @@ -0,0 +1,21 @@ +{ + age.generators.argon2id = + { + pkgs, + lib, + decrypt, + deps, + ... + }: + let + dep = builtins.head deps; + in + '' + echo " -> Deriving argon2id hash from "${lib.escapeShellArg dep.host}":"${lib.escapeShellArg dep.name}"" >&2 + ${decrypt} ${lib.escapeShellArg dep.file} \ + | tr -d '\n' \ + | ${pkgs.libargon2}/bin/argon2 "$(${pkgs.openssl}/bin/openssl rand -base64 16)" -id -e \ + || die "Failure while generating argon2id hash" + ''; + +} diff --git a/config/basic/users.nix b/config/basic/users.nix index 5f6efd1..bd9ee3b 100644 --- a/config/basic/users.nix +++ b/config/basic/users.nix @@ -40,6 +40,8 @@ signal = uidGid 228; netbird-main = uidGid 229; paperless = uidGid 315; + stalwart-mail = uidGid 316; + build = uidGid 317; systemd-oom = uidGid 300; systemd-coredump = uidGid 301; patrick = uidGid 1000; diff --git a/config/services/idmail.nix b/config/services/idmail.nix new file mode 100644 index 0000000..b4674a2 --- /dev/null +++ b/config/services/idmail.nix @@ -0,0 +1,92 @@ +{ + inputs, + config, + ... +}: +let + domain = config.secrets.secrets.global.domains.mail_public; + idmailDomain = "alias.${domain}"; + priv_domain = config.secrets.secrets.global.domains.mail_private; + + mkRandomSecret = { + generator.script = "alnum"; + mode = "000"; + intermediary = true; + }; + + mkArgon2id = secret: { + generator.dependencies = [ config.age.secrets.${secret} ]; + generator.script = "argon2id"; + mode = "440"; + group = "stalwart-mail"; + }; +in +{ + environment.persistence."/persist".directories = [ + { + directory = config.services.idmail.dataDir; + user = "stalwart-mail"; + group = "stalwart-mail"; + mode = "4770"; + } + ]; + + age.secrets = { + idmail-user-pw_admin = mkRandomSecret; + idmail-user-hash_admin = mkArgon2id "idmail-user-pw_admin"; + idmail-mailbox-pw_catch-all = mkRandomSecret; + idmail-mailbox-hash_catch-all = mkArgon2id "idmail-mailbox-pw_catch-all"; + }; + + services.idmail = { + package = inputs.idmail.packages."aarch64-linux".default; + enable = true; + # Stalwart will change permissions due to SQLite implementation. + # Therefore, run as stalwart-mail since we don't allow reading + # stalwarts folder anyway (sandboxing is on). + user = "stalwart-mail"; + provision = { + enable = true; + users.admin = { + admin = true; + password_hash = "%{file:${config.age.secrets.idmail-user-hash_admin.path}}%"; + }; + domains = { + "${domain}" = { + owner = "admin"; + catch_all = "catch-all@${domain}"; + public = true; + }; + "${priv_domain}" = { + owner = "admin"; + catch_all = "catch-all@${domain}"; + public = false; + }; + }; + mailboxes."catch-all@${domain}" = { + password_hash = "%{file:${config.age.secrets.idmail-mailbox-hash_catch-all.path}}%"; + owner = "admin"; + }; + # XXX: create mailboxes for git@ vaultwarden@ and simultaneously alias them to the catch all for a send only mail. + }; + }; + systemd.services.idmail.serviceConfig.RestartSec = "60"; # Retry every minute + + services.nginx = { + upstreams.idmail = { + servers."127.0.0.1:3000" = { }; + extraConfig = '' + zone idmail 64k; + keepalive 2; + ''; + }; + virtualHosts.${idmailDomain} = { + forceSSL = true; + useACMEWildcardHost = true; + locations."/" = { + proxyPass = "http://idmail"; + proxyWebsockets = true; + }; + }; + }; +} diff --git a/config/services/stalwart.nix b/config/services/stalwart.nix new file mode 100644 index 0000000..17d96bc --- /dev/null +++ b/config/services/stalwart.nix @@ -0,0 +1,662 @@ +{ + config, + lib, + pkgs, + ... +}: +let + priv_domain = config.secrets.secrets.global.domains.mail_private; + domain = config.secrets.secrets.global.domains.mail_public; + mailDomains = [ + priv_domain + domain + ]; + mailBackupDir = "/var/cache/mail-backup"; + dataDir = "/var/lib/stalwart-mail"; +in +{ + age.secrets.resticpasswd = { + generator.script = "alnum"; + }; + age.secrets.stalwartHetznerSshKey = { + generator.script = "ssh-ed25519"; + }; + services.restic.backups = { + main = { + user = "root"; + timerConfig = { + OnCalendar = "06:00"; + Persistent = true; + RandomizedDelaySec = "3h"; + }; + initialize = true; + passwordFile = config.age.secrets.resticpasswd.path; + hetznerStorageBox = { + enable = true; + inherit (config.secrets.secrets.global.hetzner) mainUser; + inherit (config.secrets.secrets.global.hetzner.users.stalwart-mail) subUid path; + sshAgeSecret = "stalwartHetznerSshKey"; + }; + paths = [ + mailBackupDir + ]; + #pruneOpts = [ + # "--keep-daily 10" + # "--keep-weekly 7" + # "--keep-monthly 12" + # "--keep-yearly 75" + #]; + }; + }; + systemd.services.backup-mail = { + description = "Mail backup"; + environment = { + STALWART_DATA = dataDir; + IDMAIL_DATA = config.services.idmail.dataDir; + BACKUP_DIR = mailBackupDir; + }; + serviceConfig = { + SyslogIdentifier = "backup-mail"; + Type = "oneshot"; + User = "stalwart-mail"; + Group = "stalwart-mail"; + ExecStart = lib.getExe ( + pkgs.writeShellApplication { + name = "backup-mail"; + runtimeInputs = [ pkgs.sqlite ]; + text = '' + sqlite3 "$STALWART_DATA/database.sqlite3" ".backup '$BACKUP_DIR/database.sqlite3'" + sqlite3 "$IDMAIL_DATA/database.sqlite3" ".backup '$BACKUP_DIR/idmail.db'" + cp -r "$STALWART_DATA/dkim" "$BACKUP_DIR/" + ''; + } + ); + ReadWritePaths = [ + dataDir + config.services.idmail.dataDir + mailBackupDir + ]; + Restart = "no"; + }; + requiredBy = [ "restic-backups-main.service" ]; + before = [ "restic-backups-main.service" ]; + }; + + age.secrets.stalwart-admin-pw = { + generator.script = "alnum"; + mode = "000"; + intermediary = true; + }; + + age.secrets.stalwart-admin-hash = { + generator.dependencies = [ config.age.secrets.stalwart-admin-pw ]; + generator.script = "argon2id"; + mode = "440"; + group = "stalwart-mail"; + }; + + users.groups.acme.members = [ "stalwart-mail" ]; + + networking.firewall.allowedTCPPorts = [ + 25 # smtp + 465 # submission tls + # 587 # submission starttls + 993 # imap tls + # 143 # imap starttls + 4190 # manage sieve + ]; + environment.persistence."/persist".directories = [ + { + directory = dataDir; + user = "stalwart-mail"; + group = "stalwart-mail"; + mode = "0700"; + } + ]; + + # Needed so we don't run out of tmpfs space for large backups. + # Technically this could be cleared each boot but whatever. + environment.persistence."/state".directories = [ + { + directory = mailBackupDir; + user = "stalwart-mail"; + group = "stalwart-mail"; + mode = "0700"; + } + ]; + services.nginx = { + upstreams.stalwart = { + servers."127.0.0.1:8080" = { }; + extraConfig = '' + zone stalwart 64k; + keepalive 2; + ''; + }; + virtualHosts = + { + ${domain} = { + forceSSL = true; + useACMEWildcardHost = true; + extraConfig = '' + client_max_body_size 512M; + ''; + locations."/" = { + proxyPass = "http://stalwart"; + proxyWebsockets = true; + }; + }; + } + // lib.genAttrs + [ + "autoconfig.${domain}" + "autodiscover.${domain}" + "mta-sts.${domain}" + ] + (_: { + forceSSL = true; + useACMEWildcardHost = true; + locations."/".proxyPass = "http://stalwart"; + }); + }; + systemd.services.stalwart-mail = + let + cfg = config.services.stalwart-mail; + configFormat = pkgs.formats.toml { }; + configFile = configFormat.generate "stalwart-mail.toml" cfg.settings; + in + { + preStart = lib.mkAfter ( + '' + cat ${configFile} > /run/stalwart-mail/config.toml + cat ${config.age.secrets.stalwart-admin-hash.path} \ + | tr -d '\n' > /run/stalwart-mail/admin-hash + + mkdir -p /var/lib/stalwart-mail/dkim + '' + # Generate DKIM keys if necessary + + lib.concatLines ( + lib.forEach mailDomains (domain: '' + if [[ ! -e /var/lib/stalwart-mail/dkim/rsa-${domain}.key ]]; then + echo "Generating DKIM key for ${domain} (rsa)" + ${lib.getExe pkgs.openssl} genrsa -traditional -out /var/lib/stalwart-mail/dkim/rsa-${domain}.key 2048 + fi + if [[ ! -e /var/lib/stalwart-mail/dkim/ed25519-${domain}.key ]]; then + echo "Generating DKIM key for ${domain} (ed25519)" + ${lib.getExe pkgs.openssl} genpkey -algorithm ed25519 -out /var/lib/stalwart-mail/dkim/ed25519-${domain}.key + fi + '') + ) + ); + + serviceConfig = { + RuntimeDirectory = "stalwart-mail"; + ReadWritePaths = [ config.services.idmail.dataDir ]; + ExecStart = lib.mkForce [ + "" + "${cfg.package}/bin/stalwart-mail --config=/run/stalwart-mail/config.toml" + ]; + RestartSec = "60"; # Retry every minute + }; + }; + + services.stalwart-mail = { + enable = true; + settings = + let + case = field: check: value: data: { + "if" = field; + ${check} = value; + "then" = data; + }; + ifthen = field: data: { + "if" = field; + "then" = data; + }; + otherwise = value: { "else" = value; }; + is-smtp = case "listener" "eq" "smtp"; + is-authenticated = data: { + "if" = "!is_empty(authenticated_as)"; + "then" = data; + }; + in + lib.mkForce { + config.local-keys = [ + "store.*" + "directory.*" + "tracer.*" + "server.*" + "!server.blocked-ip.*" + "!server.allowed-ip.*" + "authentication.fallback-admin.*" + "cluster.node-id" + "storage.data" + "storage.blob" + "storage.lookup" + "storage.fts" + "storage.directory" + "lookup.default.hostname" + "certificate.*" + "auth.dkim.*" + "signature.*" + ]; + + authentication.fallback-admin = { + user = "admin"; + secret = "%{file:/run/stalwart-mail/admin-hash}%"; + }; + + tracer.stdout = { + # Do not use the built-in journal tracer, as it shows much less auxiliary + # information for the same loglevel + type = "stdout"; + level = "info"; + ansi = false; # no colour markers to journald + enable = true; + }; + + store.db = { + type = "sqlite"; + path = "${dataDir}/database.sqlite3"; + }; + + store.idmail = { + type = "sqlite"; + path = "${config.services.idmail.dataDir}/idmail.db"; + query = + let + # Remove comments from SQL and make it single-line + toSingleLineSql = + sql: + lib.concatStringsSep " " ( + lib.forEach (lib.flatten (lib.split "\n" sql)) ( + line: lib.optionalString (builtins.match "^[[:space:]]*--.*" line == null) line + ) + ); + in + { + # "SELECT name, type, secret, description, quota FROM accounts WHERE name = ?1 AND active = true"; + name = toSingleLineSql '' + SELECT + m.address AS name, + 'individual' AS type, + m.password_hash AS secret, + m.address AS description, + 0 AS quota + FROM mailboxes AS m + JOIN domains AS d ON m.domain = d.domain + JOIN users AS u ON m.owner = u.username + WHERE m.address = ?1 + AND m.active = true + AND d.active = true + AND u.active = true + ''; + # "SELECT member_of FROM group_members WHERE name = ?1"; + members = ""; + # "SELECT name FROM emails WHERE address = ?1"; + recipients = toSingleLineSql '' + -- It is important that we return only one value here, but in theory three UNIONed + -- queries are guaranteed to be distinct. This is because a mailbox address + -- and alias address can never be the same, their cross-table uniqueness is guaranteed on insert. + -- The catch-all union can also only return something if @domain.tld is given as a parameter, + -- which is invalid for aliases and mailboxes. + -- + -- Nonetheless, it may be beneficial to allow an alias to override an existing mailbox, + -- so we can have send-only mailboxes which have their incoming mail redirected somewhere else. + -- Therefore, we make sure to order the query by (aliases -> mailboxes -> catch all) and only return the + -- highest priority one. + SELECT name FROM ( + -- Select the target of a matching alias (if any) + -- but make sure that all related parts are active. + SELECT a.target AS name, 1 AS rowOrder + FROM aliases AS a + JOIN domains AS d ON a.domain = d.domain + JOIN ( + -- To check whether the owner is active we need to make a subquery + -- because the owner could be a user or mailbox + SELECT username + FROM users + WHERE active = true + UNION + SELECT m.address AS username + FROM mailboxes AS m + JOIN users AS u ON m.owner = u.username + WHERE m.active = true + AND u.active = true + ) AS u ON a.owner = u.username + WHERE a.address = ?1 + AND a.active = true + AND d.active = true + -- Select the primary mailbox address if it matches and + -- all related parts are active. + UNION + SELECT m.address AS name, 2 AS rowOrder + FROM mailboxes AS m + JOIN domains AS d ON m.domain = d.domain + JOIN users AS u ON m.owner = u.username + WHERE m.address = ?1 + AND m.active = true + AND d.active = true + AND u.active = true + -- Finally, select any catch_all address that would catch this. + -- Again make sure everything is active. + UNION + SELECT d.catch_all AS name, 3 AS rowOrder + FROM domains AS d + JOIN mailboxes AS m ON d.catch_all = m.address + JOIN users AS u ON m.owner = u.username + WHERE ?1 = ('@' || d.domain) + AND d.active = true + AND m.active = true + AND u.active = true + ORDER BY rowOrder, name ASC + LIMIT 1 + ) + ''; + # "SELECT address FROM emails WHERE name = ?1 AND type != 'list' ORDER BY type DESC, address ASC"; + emails = toSingleLineSql '' + -- Return first the primary address, then any aliases. + SELECT address FROM ( + -- Select primary address, if active + SELECT m.address AS address, 1 AS rowOrder + FROM mailboxes AS m + JOIN domains AS d ON m.domain = d.domain + JOIN users AS u ON m.owner = u.username + WHERE m.address = ?1 + AND m.active = true + AND d.active = true + AND u.active = true + -- Select any active aliases + UNION + SELECT a.address AS address, 2 AS rowOrder + FROM aliases AS a + JOIN domains AS d ON a.domain = d.domain + JOIN ( + -- To check whether the owner is active we need to make a subquery + -- because the owner could be a user or mailbox + SELECT username + FROM users + WHERE active = true + UNION + SELECT m.address AS username + FROM mailboxes AS m + JOIN users AS u ON m.owner = u.username + WHERE m.active = true + AND u.active = true + ) AS u ON a.owner = u.username + WHERE a.target = ?1 + AND a.active = true + AND d.active = true + -- Select the catch-all marker, if we are the target. + UNION + -- Order 2 is correct, it counts as an alias + SELECT ('@' || d.domain) AS address, 2 AS rowOrder + FROM domains AS d + JOIN mailboxes AS m ON d.catch_all = m.address + JOIN users AS u ON m.owner = u.username + WHERE d.catch_all = ?1 + AND d.active = true + AND m.active = true + AND u.active = true + ORDER BY rowOrder, address ASC + ) + ''; + # "SELECT address FROM emails WHERE address LIKE '%' || ?1 || '%' AND type = 'primary' ORDER BY address LIMIT 5"; + verify = toSingleLineSql '' + SELECT m.address AS address + FROM mailboxes AS m + JOIN domains AS d ON m.domain = d.domain + JOIN users AS u ON m.owner = u.username + WHERE m.address LIKE '%' || ?1 || '%' + AND m.active = true + AND d.active = true + AND u.active = true + UNION + SELECT a.address AS address + FROM aliases AS a + JOIN domains AS d ON a.domain = d.domain + JOIN ( + -- To check whether the owner is active we need to make a subquery + -- because the owner could be a user or mailbox + SELECT username + FROM users + WHERE active = true + UNION + SELECT m.address AS username + FROM mailboxes AS m + JOIN users AS u ON m.owner = u.username + WHERE m.active = true + AND u.active = true + ) AS u ON a.owner = u.username + WHERE a.address LIKE '%' || ?1 || '%' + AND a.active = true + AND d.active = true + ORDER BY address + LIMIT 5 + ''; + # "SELECT p.address FROM emails AS p JOIN emails AS l ON p.name = l.name WHERE p.type = 'primary' AND l.address = ?1 AND l.type = 'list' ORDER BY p.address LIMIT 50"; + # XXX: We don't actually expand, but return the same address if it exists since we don't support mailing lists + expand = toSingleLineSql '' + SELECT m.address AS address + FROM mailboxes AS m + JOIN domains AS d ON m.domain = d.domain + JOIN users AS u ON m.owner = u.username + WHERE m.address = ?1 + AND m.active = true + AND d.active = true + AND u.active = true + UNION + SELECT a.address AS address + FROM aliases AS a + JOIN domains AS d ON a.domain = d.domain + JOIN ( + -- To check whether the owner is active we need to make a subquery + -- because the owner could be a user or mailbox + SELECT username + FROM users + WHERE active = true + UNION + SELECT m.address AS username + FROM mailboxes AS m + JOIN users AS u ON m.owner = u.username + WHERE m.active = true + AND u.active = true + ) AS u ON a.owner = u.username + WHERE a.address = ?1 + AND a.active = true + AND d.active = true + ORDER BY address + LIMIT 50 + ''; + # "SELECT 1 FROM emails WHERE address LIKE '%@' || ?1 LIMIT 1"; + domains = toSingleLineSql '' + SELECT domain + FROM domains + WHERE domain = ?1 + ''; + }; + }; + + storage = { + data = "db"; + fts = "db"; + lookup = "db"; + blob = "db"; + directory = "idmail"; + }; + + directory.idmail = { + type = "sql"; + store = "idmail"; + columns = { + name = "name"; + description = "description"; + secret = "secret"; + email = "email"; + #quota = "quota"; + class = "type"; + }; + }; + + resolver = { + type = "system"; + public-suffix = [ + "file://${pkgs.publicsuffix-list}/share/publicsuffix/public_suffix_list.dat" + ]; + }; + + config.resource.spam-filter = "file://${config.services.stalwart-mail.package}/etc/stalwart/spamfilter.toml"; + config.resource.webadmin = "file://${config.services.stalwart-mail.package.webadmin}/webadmin.zip"; + webadmin.path = "/var/cache/stalwart-mail"; + + certificate.default = { + cert = "%{file:${config.security.acme.certs.${domain}.directory}/fullchain.pem}%"; + private-key = "%{file:${config.security.acme.certs.${domain}.directory}/key.pem}%"; + default = true; + }; + + lookup.default.hostname = domain; + server = { + tls = { + certificate = "default"; + ignore-client-order = true; + }; + socket = { + nodelay = true; + reuse-addr = true; + }; + listener = { + smtp = { + protocol = "smtp"; + bind = "[::]:25"; + }; + submissions = { + protocol = "smtp"; + bind = "[::]:465"; + tls.implicit = true; + }; + imaps = { + protocol = "imap"; + bind = "[::]:993"; + tls.implicit = true; + }; + http = { + # jmap, web interface + protocol = "http"; + bind = "[::]:8080"; + url = "https://${domain}"; + use-x-forwarded = true; + }; + sieve = { + protocol = "managesieve"; + bind = "[::]:4190"; + tls.implicit = true; + }; + }; + }; + + imap = { + request.max-size = 52428800; + auth = { + max-failures = 3; + allow-plain-text = false; + }; + timeout = { + authentication = "30m"; + anonymous = "1m"; + idle = "30m"; + }; + rate-limit = { + requests = "20000/1m"; + concurrent = 32; + }; + }; + + auth.dkim.sign = [ + (ifthen "is_local_domain('*', sender_domain)" "['rsa-' + sender_domain, 'ed25519-' + sender_domain]") + (otherwise false) + ]; + + signature = lib.mergeAttrsList ( + lib.forEach mailDomains (domain: { + "ed25519-${domain}" = { + private-key = "%{file:/var/lib/stalwart-mail/dkim/ed25519-${domain}.key}%"; + inherit domain; + selector = "ed_default"; + headers = [ + "From" + "To" + "Date" + "Subject" + "Message-ID" + ]; + algorithm = "ed25519-sha256"; + canonicalization = "relaxed/relaxed"; + set-body-length = false; + report = true; + }; + "rsa-${domain}" = { + private-key = "%{file:/var/lib/stalwart-mail/dkim/rsa-${domain}.key}%"; + inherit domain; + selector = "rsa_default"; + headers = [ + "From" + "To" + "Date" + "Subject" + "Message-ID" + ]; + algorithm = "rsa-sha256"; + canonicalization = "relaxed/relaxed"; + set-body-length = false; + report = true; + }; + }) + ); + + session.extensions = { + pipelining = true; + chunking = true; + requiretls = true; + no-soliciting = ""; + dsn = false; + expn = [ + (is-authenticated true) + (otherwise false) + ]; + vrfy = [ + (is-authenticated true) + (otherwise false) + ]; + future-release = [ + (is-authenticated "30d") + (otherwise false) + ]; + deliver-by = [ + (is-authenticated "365d") + (otherwise false) + ]; + mt-priority = [ + (is-authenticated "mixer") + (otherwise false) + ]; + }; + + session.ehlo = { + require = true; + reject-non-fqdn = [ + (is-smtp true) + (otherwise false) + ]; + }; + + session.rcpt = { + catch-all = true; + relay = [ + (is-authenticated true) + (otherwise false) + ]; + max-recipients = 25; + }; + }; + }; +} diff --git a/flake.lock b/flake.lock index 6237aaf..7e8fdc5 100644 --- a/flake.lock +++ b/flake.lock @@ -115,6 +115,23 @@ } }, "crane": { + "flake": false, + "locked": { + "lastModified": 1727316705, + "narHash": "sha256-/mumx8AQ5xFuCJqxCIOFCHTVlxHkMT21idpbgbm/TIE=", + "owner": "ipetkov", + "repo": "crane", + "rev": "5b03654ce046b5167e7b0bccbd8244cb56c16f0e", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "ref": "v0.19.0", + "repo": "crane", + "type": "github" + } + }, + "crane_2": { "inputs": { "flake-compat": [ "lanzaboote", @@ -147,7 +164,7 @@ "type": "github" } }, - "crane_2": { + "crane_3": { "flake": false, "locked": { "lastModified": 1727316705, @@ -252,7 +269,7 @@ "devshell_3": { "inputs": { "nixpkgs": [ - "nix-topology", + "idmail", "nixpkgs" ] }, @@ -271,6 +288,27 @@ } }, "devshell_4": { + "inputs": { + "nixpkgs": [ + "nix-topology", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1728330715, + "narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=", + "owner": "numtide", + "repo": "devshell", + "rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, + "devshell_5": { "inputs": { "nixpkgs": [ "nixos-extra-modules", @@ -292,7 +330,7 @@ "type": "github" } }, - "devshell_5": { + "devshell_6": { "inputs": { "nixpkgs": [ "nixp-meta", @@ -313,7 +351,7 @@ "type": "github" } }, - "devshell_6": { + "devshell_7": { "inputs": { "nixpkgs": [ "nixvim", @@ -357,13 +395,37 @@ "dream2nix": { "inputs": { "nixpkgs": [ - "nixp-meta", + "idmail", "nci", "nixpkgs" ], "purescript-overlay": "purescript-overlay", "pyproject-nix": "pyproject-nix" }, + "locked": { + "lastModified": 1732214960, + "narHash": "sha256-ViyEMSYwaza6y55XTDrsRi2K4YKCLsefMTorjWSE27s=", + "owner": "nix-community", + "repo": "dream2nix", + "rev": "a8dac99db44307fdecead13a39c584b97812d0d4", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "dream2nix", + "type": "github" + } + }, + "dream2nix_2": { + "inputs": { + "nixpkgs": [ + "nixp-meta", + "nci", + "nixpkgs" + ], + "purescript-overlay": "purescript-overlay_2", + "pyproject-nix": "pyproject-nix_2" + }, "locked": { "lastModified": 1731424167, "narHash": "sha256-nKKeRwq7mxcW8cBTmPKzSg0DR/inVrtuJudVM81GISU=", @@ -395,6 +457,20 @@ } }, "flake-compat_10": { + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "revCount": 57, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" + } + }, + "flake-compat_11": { "flake": false, "locked": { "lastModified": 1696426674, @@ -410,7 +486,23 @@ "type": "github" } }, - "flake-compat_11": { + "flake-compat_12": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_13": { "flake": false, "locked": { "lastModified": 1673956053, @@ -429,11 +521,11 @@ "flake-compat_2": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -493,11 +585,11 @@ "flake-compat_6": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", "type": "github" }, "original": { @@ -507,35 +599,6 @@ } }, "flake-compat_7": { - "locked": { - "lastModified": 1717312683, - "narHash": "sha256-FrlieJH50AuvagamEvWMIE6D2OAnERuDboFDYAED/dE=", - "owner": "nix-community", - "repo": "flake-compat", - "rev": "38fd3954cf65ce6faf3d0d45cd26059e059f07ea", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_8": { - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "revCount": 57, - "type": "tarball", - "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz" - }, - "original": { - "type": "tarball", - "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz" - } - }, - "flake-compat_9": { "flake": false, "locked": { "lastModified": 1696426674, @@ -551,6 +614,37 @@ "type": "github" } }, + "flake-compat_8": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_9": { + "locked": { + "lastModified": 1717312683, + "narHash": "sha256-FrlieJH50AuvagamEvWMIE6D2OAnERuDboFDYAED/dE=", + "owner": "nix-community", + "repo": "flake-compat", + "rev": "38fd3954cf65ce6faf3d0d45cd26059e059f07ea", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -570,6 +664,24 @@ } }, "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_2" + }, + "locked": { + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_3": { "inputs": { "nixpkgs-lib": [ "lanzaboote", @@ -590,9 +702,9 @@ "type": "github" } }, - "flake-parts_3": { + "flake-parts_4": { "inputs": { - "nixpkgs-lib": "nixpkgs-lib_2" + "nixpkgs-lib": "nixpkgs-lib_3" }, "locked": { "lastModified": 1730504689, @@ -608,7 +720,7 @@ "type": "github" } }, - "flake-parts_4": { + "flake-parts_5": { "inputs": { "nixpkgs-lib": [ "nixpkgs-wayland", @@ -630,7 +742,7 @@ "type": "github" } }, - "flake-parts_5": { + "flake-parts_6": { "inputs": { "nixpkgs-lib": [ "nixvim", @@ -820,7 +932,7 @@ "nixvim", "flake-compat" ], - "gitignore": "gitignore_6", + "gitignore": "gitignore_7", "nixpkgs": [ "nixvim", "nixpkgs" @@ -867,6 +979,28 @@ } }, "gitignore_2": { + "inputs": { + "nixpkgs": [ + "idmail", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gitignore_3": { "inputs": { "nixpkgs": [ "lanzaboote", @@ -888,7 +1022,7 @@ "type": "github" } }, - "gitignore_3": { + "gitignore_4": { "inputs": { "nixpkgs": [ "nix-topology", @@ -910,7 +1044,7 @@ "type": "github" } }, - "gitignore_4": { + "gitignore_5": { "inputs": { "nixpkgs": [ "nixos-extra-modules", @@ -932,7 +1066,7 @@ "type": "github" } }, - "gitignore_5": { + "gitignore_6": { "inputs": { "nixpkgs": [ "nixp-meta", @@ -954,7 +1088,7 @@ "type": "github" } }, - "gitignore_6": { + "gitignore_7": { "inputs": { "nixpkgs": [ "nixvim", @@ -976,7 +1110,7 @@ "type": "github" } }, - "gitignore_7": { + "gitignore_8": { "inputs": { "nixpkgs": [ "pre-commit-hooks", @@ -1076,6 +1210,31 @@ "type": "github" } }, + "idmail": { + "inputs": { + "devshell": "devshell_3", + "flake-parts": "flake-parts_2", + "nci": "nci", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks": "pre-commit-hooks_2", + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1732405639, + "narHash": "sha256-/amsyYIdOMr3ZUrkbiFYEDtYfNeRw+mWExCisQL2Cj0=", + "owner": "oddlama", + "repo": "idmail", + "rev": "0e7170cc5888f33c3c630390f9601771ae0bb2b8", + "type": "github" + }, + "original": { + "owner": "oddlama", + "repo": "idmail", + "type": "github" + } + }, "impermanence": { "locked": { "lastModified": 1731242966, @@ -1121,15 +1280,15 @@ }, "lanzaboote": { "inputs": { - "crane": "crane", - "flake-compat": "flake-compat_2", - "flake-parts": "flake-parts_2", + "crane": "crane_2", + "flake-compat": "flake-compat_4", + "flake-parts": "flake-parts_3", "flake-utils": "flake-utils_2", "nixpkgs": [ "nixpkgs" ], "pre-commit-hooks-nix": "pre-commit-hooks-nix", - "rust-overlay": "rust-overlay" + "rust-overlay": "rust-overlay_2" }, "locked": { "lastModified": 1682802423, @@ -1149,7 +1308,7 @@ "lib-aggregate": { "inputs": { "flake-utils": "flake-utils_6", - "nixpkgs-lib": "nixpkgs-lib_3" + "nixpkgs-lib": "nixpkgs-lib_4" }, "locked": { "lastModified": 1731845570, @@ -1216,16 +1375,59 @@ "type": "github" } }, + "mk-naked-shell_2": { + "flake": false, + "locked": { + "lastModified": 1681286841, + "narHash": "sha256-3XlJrwlR0nBiREnuogoa5i1b4+w/XPe0z8bbrJASw0g=", + "owner": "yusdacra", + "repo": "mk-naked-shell", + "rev": "7612f828dd6f22b7fb332cc69440e839d7ffe6bd", + "type": "github" + }, + "original": { + "owner": "yusdacra", + "repo": "mk-naked-shell", + "type": "github" + } + }, "nci": { "inputs": { - "crane": "crane_2", + "crane": "crane", "dream2nix": "dream2nix", "mk-naked-shell": "mk-naked-shell", - "nixpkgs": "nixpkgs_2", + "nixpkgs": [ + "idmail", + "nixpkgs" + ], "parts": "parts", - "rust-overlay": "rust-overlay_2", + "rust-overlay": "rust-overlay", "treefmt": "treefmt" }, + "locked": { + "lastModified": 1732342495, + "narHash": "sha256-7qfvmnJQByEtl5bS+rTydLCe3Saz9kMRaJxPCdqb1wQ=", + "owner": "yusdacra", + "repo": "nix-cargo-integration", + "rev": "ae9de2d06519a3bb26b649e1c0d1cfa22c20dc0e", + "type": "github" + }, + "original": { + "owner": "yusdacra", + "repo": "nix-cargo-integration", + "type": "github" + } + }, + "nci_2": { + "inputs": { + "crane": "crane_3", + "dream2nix": "dream2nix_2", + "mk-naked-shell": "mk-naked-shell_2", + "nixpkgs": "nixpkgs_2", + "parts": "parts_2", + "rust-overlay": "rust-overlay_3", + "treefmt": "treefmt_2" + }, "locked": { "lastModified": 1731605339, "narHash": "sha256-O0vWXiC1pBYXgdsKbQGw0Jev8Sc6dxR9Up0NKgIeH9g=", @@ -1263,10 +1465,10 @@ }, "nix-eval-jobs": { "inputs": { - "flake-parts": "flake-parts_4", + "flake-parts": "flake-parts_5", "nix-github-actions": "nix-github-actions", "nixpkgs": "nixpkgs_7", - "treefmt-nix": "treefmt-nix_2" + "treefmt-nix": "treefmt-nix_3" }, "locked": { "lastModified": 1731682758, @@ -1326,10 +1528,10 @@ }, "nix-topology": { "inputs": { - "devshell": "devshell_3", + "devshell": "devshell_4", "flake-utils": "flake-utils_4", "nixpkgs": "nixpkgs", - "pre-commit-hooks": "pre-commit-hooks_2" + "pre-commit-hooks": "pre-commit-hooks_3" }, "locked": { "lastModified": 1730803396, @@ -1362,13 +1564,13 @@ }, "nixos-extra-modules": { "inputs": { - "devshell": "devshell_4", + "devshell": "devshell_5", "flake-utils": "flake-utils_5", "lib-net": "lib-net", "nixpkgs": [ "nixpkgs" ], - "pre-commit-hooks": "pre-commit-hooks_3" + "pre-commit-hooks": "pre-commit-hooks_4" }, "locked": { "lastModified": 1732216602, @@ -1443,12 +1645,12 @@ }, "nixp-meta": { "inputs": { - "devshell": "devshell_5", - "flake-parts": "flake-parts_3", - "nci": "nci", + "devshell": "devshell_6", + "flake-parts": "flake-parts_4", + "nci": "nci_2", "nixpkgs": "nixpkgs_3", - "pre-commit-hooks": "pre-commit-hooks_4", - "treefmt-nix": "treefmt-nix" + "pre-commit-hooks": "pre-commit-hooks_5", + "treefmt-nix": "treefmt-nix_2" }, "locked": { "lastModified": 1731864011, @@ -1505,6 +1707,18 @@ } }, "nixpkgs-lib_3": { + "locked": { + "lastModified": 1730504152, + "narHash": "sha256-lXvH/vOfb4aGYyvFmZK/HlsNsr/0CVWlwYvo2rxJk3s=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + } + }, + "nixpkgs-lib_4": { "locked": { "lastModified": 1731805462, "narHash": "sha256-yhEMW4MBi+IAyEJyiKbnFvY1uARyMKJpLUhkczI49wk=", @@ -1552,6 +1766,22 @@ } }, "nixpkgs-stable_2": { + "locked": { + "lastModified": 1730741070, + "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable_3": { "locked": { "lastModified": 1678872516, "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", @@ -1567,7 +1797,7 @@ "type": "github" } }, - "nixpkgs-stable_3": { + "nixpkgs-stable_4": { "locked": { "lastModified": 1685801374, "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", @@ -1583,22 +1813,6 @@ "type": "github" } }, - "nixpkgs-stable_4": { - "locked": { - "lastModified": 1730741070, - "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-24.05", - "repo": "nixpkgs", - "type": "github" - } - }, "nixpkgs-stable_5": { "locked": { "lastModified": 1730741070, @@ -1615,9 +1829,25 @@ "type": "github" } }, + "nixpkgs-stable_6": { + "locked": { + "lastModified": 1730741070, + "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-wayland": { "inputs": { - "flake-compat": "flake-compat_7", + "flake-compat": "flake-compat_9", "lib-aggregate": "lib-aggregate", "nix-eval-jobs": "nix-eval-jobs", "nixpkgs": [ @@ -1752,9 +1982,9 @@ }, "nixvim": { "inputs": { - "devshell": "devshell_6", - "flake-compat": "flake-compat_8", - "flake-parts": "flake-parts_5", + "devshell": "devshell_7", + "flake-compat": "flake-compat_10", + "flake-parts": "flake-parts_6", "git-hooks": "git-hooks", "home-manager": "home-manager_2", "nix-darwin": "nix-darwin", @@ -1762,7 +1992,7 @@ "nixpkgs" ], "nuschtosSearch": "nuschtosSearch", - "treefmt-nix": "treefmt-nix_3" + "treefmt-nix": "treefmt-nix_4" }, "locked": { "lastModified": 1731780782, @@ -1802,6 +2032,28 @@ } }, "parts": { + "inputs": { + "nixpkgs-lib": [ + "idmail", + "nci", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "parts_2": { "inputs": { "nixpkgs-lib": [ "nixp-meta", @@ -1861,12 +2113,12 @@ "lanzaboote", "flake-utils" ], - "gitignore": "gitignore_2", + "gitignore": "gitignore_3", "nixpkgs": [ "lanzaboote", "nixpkgs" ], - "nixpkgs-stable": "nixpkgs-stable_2" + "nixpkgs-stable": "nixpkgs-stable_3" }, "locked": { "lastModified": 1681413034, @@ -1885,7 +2137,31 @@ "pre-commit-hooks_2": { "inputs": { "flake-compat": "flake-compat_3", - "gitignore": "gitignore_3", + "gitignore": "gitignore_2", + "nixpkgs": [ + "idmail", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable_2" + }, + "locked": { + "lastModified": 1732021966, + "narHash": "sha256-mnTbjpdqF0luOkou8ZFi2asa1N3AA2CchR/RqCNmsGE=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "3308484d1a443fc5bc92012435d79e80458fe43c", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "pre-commit-hooks_3": { + "inputs": { + "flake-compat": "flake-compat_5", + "gitignore": "gitignore_4", "nixpkgs": [ "nix-topology", "nixpkgs" @@ -1909,19 +2185,19 @@ "type": "github" } }, - "pre-commit-hooks_3": { + "pre-commit-hooks_4": { "inputs": { - "flake-compat": "flake-compat_4", + "flake-compat": "flake-compat_6", "flake-utils": [ "nixos-extra-modules", "flake-utils" ], - "gitignore": "gitignore_4", + "gitignore": "gitignore_5", "nixpkgs": [ "nixos-extra-modules", "nixpkgs" ], - "nixpkgs-stable": "nixpkgs-stable_3" + "nixpkgs-stable": "nixpkgs-stable_4" }, "locked": { "lastModified": 1702456155, @@ -1937,12 +2213,12 @@ "type": "github" } }, - "pre-commit-hooks_4": { + "pre-commit-hooks_5": { "inputs": { - "flake-compat": "flake-compat_6", - "gitignore": "gitignore_5", + "flake-compat": "flake-compat_8", + "gitignore": "gitignore_6", "nixpkgs": "nixpkgs_4", - "nixpkgs-stable": "nixpkgs-stable_4" + "nixpkgs-stable": "nixpkgs-stable_5" }, "locked": { "lastModified": 1731363552, @@ -1958,14 +2234,14 @@ "type": "github" } }, - "pre-commit-hooks_5": { + "pre-commit-hooks_6": { "inputs": { - "flake-compat": "flake-compat_9", - "gitignore": "gitignore_7", + "flake-compat": "flake-compat_11", + "gitignore": "gitignore_8", "nixpkgs": [ "nixpkgs" ], - "nixpkgs-stable": "nixpkgs-stable_5" + "nixpkgs-stable": "nixpkgs-stable_6" }, "locked": { "lastModified": 1731363552, @@ -1983,14 +2259,39 @@ }, "purescript-overlay": { "inputs": { - "flake-compat": "flake-compat_5", + "flake-compat": "flake-compat_2", + "nixpkgs": [ + "idmail", + "nci", + "dream2nix", + "nixpkgs" + ], + "slimlock": "slimlock" + }, + "locked": { + "lastModified": 1728546539, + "narHash": "sha256-Sws7w0tlnjD+Bjck1nv29NjC5DbL6nH5auL9Ex9Iz2A=", + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "rev": "4ad4c15d07bd899d7346b331f377606631eb0ee4", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "type": "github" + } + }, + "purescript-overlay_2": { + "inputs": { + "flake-compat": "flake-compat_7", "nixpkgs": [ "nixp-meta", "nci", "dream2nix", "nixpkgs" ], - "slimlock": "slimlock" + "slimlock": "slimlock_2" }, "locked": { "lastModified": 1728546539, @@ -2023,6 +2324,23 @@ "type": "github" } }, + "pyproject-nix_2": { + "flake": false, + "locked": { + "lastModified": 1702448246, + "narHash": "sha256-hFg5s/hoJFv7tDpiGvEvXP0UfFvFEDgTdyHIjDVHu1I=", + "owner": "davhau", + "repo": "pyproject.nix", + "rev": "5a06a2697b228c04dd2f35659b4b659ca74f7aeb", + "type": "github" + }, + "original": { + "owner": "davhau", + "ref": "dream2nix", + "repo": "pyproject.nix", + "type": "github" + } + }, "root": { "inputs": { "agenix": "agenix", @@ -2031,6 +2349,7 @@ "disko": "disko", "flake-parts": "flake-parts", "home-manager": "home-manager", + "idmail": "idmail", "impermanence": "impermanence", "lanzaboote": "lanzaboote", "microvm": "microvm", @@ -2045,7 +2364,7 @@ "nixpkgs-octoprint": "nixpkgs-octoprint", "nixpkgs-wayland": "nixpkgs-wayland", "nixvim": "nixvim", - "pre-commit-hooks": "pre-commit-hooks_5", + "pre-commit-hooks": "pre-commit-hooks_6", "spicetify-nix": "spicetify-nix", "stylix": "stylix", "systems": "systems_12", @@ -2053,6 +2372,28 @@ } }, "rust-overlay": { + "inputs": { + "nixpkgs": [ + "idmail", + "nci", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1732328983, + "narHash": "sha256-RHt12f/slrzDpSL7SSkydh8wUE4Nr4r23HlpWywed9E=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "ed8aa5b64f7d36d9338eb1d0a3bb60cf52069a72", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "rust-overlay_2": { "inputs": { "flake-utils": [ "lanzaboote", @@ -2077,7 +2418,7 @@ "type": "github" } }, - "rust-overlay_2": { + "rust-overlay_3": { "inputs": { "nixpkgs": [ "nixp-meta", @@ -2100,6 +2441,30 @@ } }, "slimlock": { + "inputs": { + "nixpkgs": [ + "idmail", + "nci", + "dream2nix", + "purescript-overlay", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688756706, + "narHash": "sha256-xzkkMv3neJJJ89zo3o2ojp7nFeaZc2G0fYwNXNJRFlo=", + "owner": "thomashoneyman", + "repo": "slimlock", + "rev": "cf72723f59e2340d24881fd7bf61cb113b4c407c", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "slimlock", + "type": "github" + } + }, + "slimlock_2": { "inputs": { "nixpkgs": [ "nixp-meta", @@ -2141,7 +2506,7 @@ }, "spicetify-nix": { "inputs": { - "flake-compat": "flake-compat_10", + "flake-compat": "flake-compat_12", "nixpkgs": [ "nixpkgs" ] @@ -2166,7 +2531,7 @@ "base16-fish": "base16-fish", "base16-helix": "base16-helix", "base16-vim": "base16-vim", - "flake-compat": "flake-compat_11", + "flake-compat": "flake-compat_13", "flake-utils": "flake-utils_8", "gnome-shell": "gnome-shell", "home-manager": "home-manager_3", @@ -2438,17 +2803,17 @@ "treefmt": { "inputs": { "nixpkgs": [ - "nixp-meta", + "idmail", "nci", "nixpkgs" ] }, "locked": { - "lastModified": 1730321837, - "narHash": "sha256-vK+a09qq19QNu2MlLcvN4qcRctJbqWkX7ahgPZ/+maI=", + "lastModified": 1732292307, + "narHash": "sha256-5WSng844vXt8uytT5djmqBCkopyle6ciFgteuA9bJpw=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "746901bb8dba96d154b66492a29f5db0693dbfcc", + "rev": "705df92694af7093dfbb27109ce16d828a79155f", "type": "github" }, "original": { @@ -2458,6 +2823,27 @@ } }, "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "idmail", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1732292307, + "narHash": "sha256-5WSng844vXt8uytT5djmqBCkopyle6ciFgteuA9bJpw=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "705df92694af7093dfbb27109ce16d828a79155f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "treefmt-nix_2": { "inputs": { "nixpkgs": "nixpkgs_5" }, @@ -2475,7 +2861,7 @@ "type": "github" } }, - "treefmt-nix_2": { + "treefmt-nix_3": { "inputs": { "nixpkgs": [ "nixpkgs-wayland", @@ -2497,7 +2883,7 @@ "type": "github" } }, - "treefmt-nix_3": { + "treefmt-nix_4": { "inputs": { "nixpkgs": [ "nixvim", @@ -2517,6 +2903,28 @@ "repo": "treefmt-nix", "type": "github" } + }, + "treefmt_2": { + "inputs": { + "nixpkgs": [ + "nixp-meta", + "nci", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730321837, + "narHash": "sha256-vK+a09qq19QNu2MlLcvN4qcRctJbqWkX7ahgPZ/+maI=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "746901bb8dba96d154b66492a29f5db0693dbfcc", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index e4301b7..efe4609 100644 --- a/flake.nix +++ b/flake.nix @@ -4,6 +4,10 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixp-meta.url = "git+https://forge.lel.lol/patrick/nixp-meta.git"; + idmail = { + url = "github:oddlama/idmail/"; + inputs.nixpkgs.follows = "nixpkgs"; + }; nixpkgs-octoprint.url = "github:patrickdag/nixpkgs/octoprint-update"; nixpkgs-wayland = { diff --git a/hosts/mailnix/default.nix b/hosts/mailnix/default.nix index 427303d..9df86cd 100644 --- a/hosts/mailnix/default.nix +++ b/hosts/mailnix/default.nix @@ -3,6 +3,8 @@ ../../config/basic ../../config/support/initrd-ssh.nix ../../config/support/zfs.nix + ../../config/services/idmail.nix + ../../config/services/stalwart.nix ./net.nix ./fs.nix diff --git a/hosts/mailnix/net.nix b/hosts/mailnix/net.nix index 9fd06dd..b4daf3f 100644 --- a/hosts/mailnix/net.nix +++ b/hosts/mailnix/net.nix @@ -53,14 +53,14 @@ }; networking.nftables.firewall.zones.untrusted.interfaces = [ "lan01" ]; security.acme.certs = { - # mail_public = { - # domain = config.secrets.secrets.global.domains.mail_public; - # extraDomainNames = [ "*.${config.secrets.secrets.global.domains.mail_public}" ]; - # }; - # mail_private = { - # domain = config.secrets.secrets.global.domains.mail_private; - # extraDomainNames = [ "*.${config.secrets.secrets.global.domains.mail_private}" ]; - # }; + "${config.secrets.secrets.global.domains.mail_public}" = { + domain = config.secrets.secrets.global.domains.mail_public; + extraDomainNames = [ "*.${config.secrets.secrets.global.domains.mail_public}" ]; + }; + "${config.secrets.secrets.global.domains.mail_private}" = { + domain = config.secrets.secrets.global.domains.mail_private; + extraDomainNames = [ "*.${config.secrets.secrets.global.domains.mail_private}" ]; + }; }; environment.persistence."/state".directories = [ { diff --git a/hosts/mailnix/secrets/generated/idmail-mailbox-hash_catch-all.age b/hosts/mailnix/secrets/generated/idmail-mailbox-hash_catch-all.age new file mode 100644 index 0000000..03e9d70 --- /dev/null +++ b/hosts/mailnix/secrets/generated/idmail-mailbox-hash_catch-all.age @@ -0,0 +1,17 @@ +age-encryption.org/v1 +-> X25519 7NpA9hDsF1TwTVRvAKHpovHSUCr0Gg11mzsubZemyDs +3eE/PWJizZWIDMr3Dt6012F6db/nlmhpM8y06eLO48s +-> piv-p256 ZFgiIw Alb1ynSHX7YiSZkhDbId9MGoQeRacJJ9Mv4a64WVxXdE +gTRZ2XC+K6bZ9my7B28oXJGfQ2fvHFZHDTGWfjzQMdA +-> piv-p256 XTQkUA AhrEyyEHX3BxETBIWDmbK5pfzmfcFCmTWX2psLAzGhYS +XFR2JtZJikRDATiZ8eflzShfvrrUMLp00s2+0N54tiI +-> piv-p256 ZFgiIw A/MHWK7H85OTk0JLH8y0t7QHcG4xRNwYwEuWPuVBLojT +Vbwyekt8SwUfJzfyualAekCf/MGW+Igs/ZALTydd9Qc +-> piv-p256 5vmPtQ Av/BlH1sZh++RL4fh2NS2HN7yipM9nLfT90OiRh9Flbj +kzi2VBRvIxbBbze4iBahMGROtnSOKznXzCNS0PR9TG0 +-> N"-grease p, Pb+NMCL ^ +BeBdV3XQgNJVO309KZx1hphkECZLRrCqPmEoR3pEzB4I3L8Q6ur+4ALEy2mLjmSp +Mir7Hdy3Pg +--- Klvxozur3RYybVYWbakGVXiTymaTfOoFXcwnj7hsEAY +믰zRx-r@`֫?kaL53ܘ}Bʡ7-6B»th릂$h&B.7O.kgm5 +PڏkѱW41Gy1% X25519 Ud9UzEUeDmMIb90vOTWVkdDvIcebEwSzI4Ii8M5jAUI +4rloQ7OzT0voyVboOaWLvOxvrlYxtcOY91dt1lq6wtg +-> piv-p256 ZFgiIw Aro3d4Lv0WTRa1OiE1f0hROViqhes5elbt5a+uKCS0y7 +UZFViBihW5si4+JbzN1OyzWDuWiFwWfoVls+EH+EUmk +-> piv-p256 XTQkUA A0mE5ni66UlnsafkVu3MK0N6aTX2UtV+jADROmg4M1aN +cYqc/9CCT1PC3inzqfQvK59MCHHNEtIhpvOvqL7E2nA +-> piv-p256 ZFgiIw AnFFxNY3lsY4fsze7Hm4vAmK7zZKGA4qEfSUH5aIkQ4j +1OwdPteTYQCWrt4IkRhflolMXJ+FUMm91n3p7icqnsc +-> piv-p256 5vmPtQ Amg+62BwmCb9ZQmZ74PzT0/FheaK2OzfyGgbHYcyo5Cl +OnlF+hKq6p91i3Jk+iwYQ2ByRTgmZX57mIAIpMRoCD8 +-> >aAO.fE-grease ' 7nl% c#t R]jw \ No newline at end of file diff --git a/hosts/mailnix/secrets/generated/idmail-user-hash_admin.age b/hosts/mailnix/secrets/generated/idmail-user-hash_admin.age new file mode 100644 index 0000000..26bffe7 Binary files /dev/null and b/hosts/mailnix/secrets/generated/idmail-user-hash_admin.age differ diff --git a/hosts/mailnix/secrets/generated/idmail-user-pw_admin.age b/hosts/mailnix/secrets/generated/idmail-user-pw_admin.age new file mode 100644 index 0000000..8ac11c2 --- /dev/null +++ b/hosts/mailnix/secrets/generated/idmail-user-pw_admin.age @@ -0,0 +1,16 @@ +age-encryption.org/v1 +-> X25519 wbI8kJZQwT22oltbFeQSWX/Kdb+QPGy/yq5FWi4tURE +QkCRyLTSekF7e+9tFhlr36Rn0vmEZ4Ad7nmYFyDrUIE +-> piv-p256 ZFgiIw AnlFacrcAVmYBv6lWsStu/GyihNjZxqBiRArNYuD6UOk +L8kywowjuGRQ7YshqlitmhvD5DlTKtnhdh0ufeybyEw +-> piv-p256 XTQkUA Anc9FjiKfMpMqjgLBF3gyTdTpdOkmQisBWNSQLPkn7qa +Nc6+wZvNadzIo1pI0Q1xV0LEuWZ5nLMC6e3PwtpviX8 +-> piv-p256 ZFgiIw Aqym6w/aiUpbfE44JfxlV0eAS/naenJCHZNWqzwypHLN +ppB2QXwm0i64LaQnyG9NxjT2MRaI7v6Xj8L3KxB7s8E +-> piv-p256 5vmPtQ ArYm0jb3ONfijVVVWJHVUXfPm+hOJAIVn2+RUAXmIW0e +/rijgC2Iok8VcAbBTeWgQ7KB2u9T7TA03WfE9Ju3UP4 +-> M^g-grease P Jl/S]j +DY4S+rVP2pA38O2f+JWEC5ODoxEXnRdO37COHof6cB85a7a64+FWT1QcAI+P+CEx +Y6FiZ2sYbqwePti5qjUxjg +--- KfDaVg+3cbtTkC/1i2dIORAEjPLrwS5ot4szuOMqMZ0 +u2eN}5Wب>|P)濄XF'l󰘍OQZ![>Yٖ֣c@W;q?'M \ No newline at end of file diff --git a/hosts/mailnix/secrets/generated/resticpasswd.age b/hosts/mailnix/secrets/generated/resticpasswd.age new file mode 100644 index 0000000..ca37fbd --- /dev/null +++ b/hosts/mailnix/secrets/generated/resticpasswd.age @@ -0,0 +1,16 @@ +age-encryption.org/v1 +-> X25519 PdEHSeb3vou1ceHtkrlTbsu5BGWZ2onVCXPCwmW8znk +q8UKSDCiI+oZp+iODHddauFYFbLdc82tEo+Bsu2bgbo +-> piv-p256 ZFgiIw AnZuTRltFip1RHFY1dr+uTJPGbAYFzWpU/HEiZYuMIgz +r0nxJt1eZsXsnCnQ0Ls+kYqyz/PJCUjef9uvziqMqls +-> piv-p256 XTQkUA Au50Oa5SpTyUFjF4W6ETiofTruRqQItE94SmHRPzR4Y2 +T9m1cYYtJr8TQuZYquoJUM+uDeim8llDiMVk3N+kDqk +-> piv-p256 ZFgiIw A/6WS2AnElPTKjwYT6K7CWnL8bolB6HNlQnuqjQ8lKt+ +/0StgIwLSpVT7NyOJLxsPJz9TtfAOZU+qWls8gYkkFE +-> piv-p256 5vmPtQ A92v/hxaXEVRNqrsNhFuKCn5TllPrJCGk1e726IDBVo+ ++yCS8ZD3uO4UWwMhk9xqWSWZ3UGgmBkIAqAtBGKF8Nw +-> a^_IFyLy-grease +smwxe0ZqF7Qc1wsp0rYM20J5FjFiTQV2UpYfUUgt3edM0+iMmBzHG9EPxKjGNmt9 +yogZ0dRKId6mKtaNJeLHUDaCMhIsYAcrhNVGDvG9JOPdhRx9Og0 +--- sG4CDChcMPfQS4gtEDGd+bH/WKNXi5ohWX4NTNkaAi0 +⹎od6?K|$ d(@) ӷ#Eq(!jY`hlL!_ \ No newline at end of file diff --git a/hosts/mailnix/secrets/generated/stalwart-admin-hash.age b/hosts/mailnix/secrets/generated/stalwart-admin-hash.age new file mode 100644 index 0000000..a341ba5 Binary files /dev/null and b/hosts/mailnix/secrets/generated/stalwart-admin-hash.age differ diff --git a/hosts/mailnix/secrets/generated/stalwart-admin-pw.age b/hosts/mailnix/secrets/generated/stalwart-admin-pw.age new file mode 100644 index 0000000..20f5c17 --- /dev/null +++ b/hosts/mailnix/secrets/generated/stalwart-admin-pw.age @@ -0,0 +1,15 @@ +age-encryption.org/v1 +-> X25519 GPymk3LLzkZtbBTHtb5BryUrBoDLImS86IoNS78OqlE +YrRCbTE595ZhRw6VxiBS9lTWB9yP4kijFqSFFdIiUpQ +-> piv-p256 ZFgiIw AsaTyNrw7YuguAOnLv5BFyU2lW61yY++gJmgNq2M+0wq +VGtlEXVaKpzomsLzjEiBtFE3q0emFLHsiWdahPS/WJU +-> piv-p256 XTQkUA A+Jsj+fWxo26HKlA5TOM2nB5WggS6TVRyfhKzNFQxpI2 +RwQp5jlvHByeXPPsov5wMEuZ2pED/iFpVBVXVrKshH4 +-> piv-p256 ZFgiIw A6HBCYbgWEEBsBQpJfiRwu672I9QOI2JF9eSeCztlBKJ +LOcgLvCIGWvs9Vhc1VuvGlYWKbnkJdngVhBDbdoMSLs +-> piv-p256 5vmPtQ A1VVL35NHnMdTROSGAKYG6V32v2D7KVo9eHuRPqejzas +WvdUexTb/Di4mv5owD/3ug2nn8Le/TMgJ+hZYbuED6c +-> M$iT~z2-grease SDOB\mE" Zxfxg kZ\' LB@$4 + +--- 2KhnAceJmwDjVhuEx3saTPzXbDOAjFcpp4DH2lgqsZE +QIu{ʼEv7s^eL%Akih_$r"z0AIq* ScL \ No newline at end of file diff --git a/hosts/mailnix/secrets/generated/stalwartHetznerSshKey.age b/hosts/mailnix/secrets/generated/stalwartHetznerSshKey.age new file mode 100644 index 0000000..e379832 --- /dev/null +++ b/hosts/mailnix/secrets/generated/stalwartHetznerSshKey.age @@ -0,0 +1,16 @@ +age-encryption.org/v1 +-> X25519 rLdYm0p5eFCwaK8u7dz/Qco//mCdnylMwhLo6nX28R4 +0XcnRiSWtCyxn1YISgdt/zVIFKPBPbKOueh+L1f62Fc +-> piv-p256 ZFgiIw A6fWtzhy3ylrbXZG4xjSGRh3Qrk7ZwMS7Fawt0XZvESm +wAMOQQvRnMCJ5DriuLHRsc9zJe5UazJBVvNNy97jJos +-> piv-p256 XTQkUA ArhoKZeRdRGXbHOcLiPcT1AruJEE7hckq7QiGKLfcm9d +YXh4slVMY/U+DfCBW6V/4Uf60Zb8RPyd0PrAHHB8xDE +-> piv-p256 ZFgiIw AsjeXhDC4x+6TPG902gZSlW9qFC0JVoznTVmnpQgip9f +ZxDSKVSBiGCGVE1w+8yZwEJx59DkdFy/6Iq1tbHQ41I +-> piv-p256 5vmPtQ AmlUti+62DpPs4k9HN+ZdKry9pwPjS1HAtnTq9xm1zT1 +zTmFw+xHDQSLkDyVXC8MtlxD5cw/tQ1yK5zlYoDKv8Y +-> w0-grease /mVZ/4hd jq'R +fvJoC6ucvHgsXQysHHQhXQQ3TMUhFIPpSHwOURHSHn/+9qFVd02Ey0DWl9LujA +--- 5VjQP6nmIwBXtA/0+zL+EQt9eZHtyp6oD6u5IPgW1s8 +0s((ىX5UJp}:yAr_ã6 +XDF9֟?1]4{Ehƍꔿ&~SA֭ S dVJeS0/WGp^fB"^ӶԂN:EiB޷lK4I1'K2>^ݽL)x\q.#.Yr8scNRQ66 xZ)عBv)ʞ4" $K d Mp*=Ρ垫Os3CE#;!..4IT{fTlѺ{ձbxHn7^ˏ_%5fqPLl`0?-!uEJ[%bENWC,E11a1{EPcXTsB m]RH"#vaXA(qc`՟&Oap%` \ No newline at end of file diff --git a/secrets/secrets.nix.age b/secrets/secrets.nix.age index 96c9c4e..2afb9d6 100644 Binary files a/secrets/secrets.nix.age and b/secrets/secrets.nix.age differ