diff --git a/.sops.yaml b/.sops.yaml index 90a3cde..723d244 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -1,9 +1,11 @@ keys: - &pickwick age13cfe8fhp4m978qlcur46vkkxepsl93ggwe53kmhue9xtpgr5zu5q4y6ln2 - &lemur age1pdk6dmyxqhdaja5d0nf8f9qjd43hmfahmkure5yrf8al9jyfmd8qfdxwl6 + - &dango age15vscvpe79l287h8f3hssrj2r45xy0l3ns94zfue2fxlq43cqdsxq58vq3c creation_rules: - path_regex: home/email/secrets.yaml$ key_groups: - age: - *pickwick - *lemur + - *dango diff --git a/config/aerospace/aerospace.toml b/config/aerospace/aerospace.toml index 81c63e0..80dfb12 100644 --- a/config/aerospace/aerospace.toml +++ b/config/aerospace/aerospace.toml @@ -13,7 +13,7 @@ exec-on-workspace-change = ['/bin/bash', '-c', '/run/current-system/sw/bin/sketchybar --trigger aerospace_workspace_changed FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE PREV_WORKSPACE=$AEROSPACE_PREV_WORKSPACE && /etc/profiles/per-user/rayandrew/bin/aerospace-scratchpad hook pull-window $AEROSPACE_PREV_WORKSPACE $AEROSPACE_FOCUSED_WORKSPACE' ] on-focus-changed = [ - 'move-mouse window-lazy-center', + # 'move-mouse window-lazy-center', 'exec-and-forget /bin/bash -c /run/current-system/sw/bin/sketchybar --trigger front_app_switched', 'exec-and-forget /run/current-system/sw/bin/sketchybar --trigger update_windows' ] diff --git a/home/email/secrets.yaml b/home/email/secrets.yaml index f0b7bf6..7ff0ec7 100644 --- a/home/email/secrets.yaml +++ b/home/email/secrets.yaml @@ -1,31 +1,35 @@ -personal: ENC[AES256_GCM,data:aaZAYmnoQfGIH6bBneKtTA==,iv:xiy1eCyBhFulfRXGz0WDFLaqPj3kXsMD4Xkk7D4s5XA=,tag:Nk0KsgICo505VEMwVUt3TA==,type:str] -uchicago: ENC[AES256_GCM,data:3ZkuIfvzOkKfQD0iyTk=,iv:aCfFrxDM5Ly/qLdLAQkK2tOxb89dFkCc9RhN5GVSGRw=,tag:7D54pkVX2F6IhM38JOUFQg==,type:str] +personal: ENC[AES256_GCM,data:MG1ryntM2YKyjb0YH/8GYg==,iv:SvwAc1dBl8wc1YWdnOuCNAb5RUNA2vLX87uzOGOaqec=,tag:ixVU3FWGpZqZx/1PD3/3KA==,type:str] +uchicago: ENC[AES256_GCM,data:LqZTprY/9Li8ab9i0dg=,iv:tLJHXwsoL7PnwonHlnXiaLahr0+fIizsoshE0GFbkn8=,tag:FT5brU6pW7xFxnzpBYU2jA==,type:str] sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] age: - recipient: age13cfe8fhp4m978qlcur46vkkxepsl93ggwe53kmhue9xtpgr5zu5q4y6ln2 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMMTcwTEc5VTdEMlZSK1FI - c2VVdjJOc3B3V2RGS0pIVGQralJ4MGFwSkRvCm5mVTNxMHUzNythNHVyRG52bVYz - RVp2d1hKUnpucko1SG01SDdWNFZGV1EKLS0tIDB2OGF1UlUyN2xnRm1WR1RleUhN - S1hFWjhCMlNZVC83ZzR4NittcXlCQlUKQO6NHCMwWKwrfwwnwLK/sO4HO6ES+PyT - dh3tRhPlv7/viO+MtHqUfQ5cbW+OWoic4prfK/UxIz5VeY331kpq+g== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxUjF4Nm0yVWFwMUM1Y2hn + SVRhUmEweDNVd3Z2NVpEVW1yaDRYaDhLZ0NZCjE3WkVTWXUrKzE1VGN5ZUtSb2VZ + WnJFRkkwZmJKTm5OWlpMLzlCZHR6aE0KLS0tIFZ3UEFmcTJIYlcrUXhyU1hSMFRK + MjhvU0NQamQzcE1sWG5FSmpoajdNc0UKslJWrvq4BIeMoZ6ZSA6anlldGOpUuXrL + PV7pVpTihxWtzPbPV36oPRVoN3GzNZXUPJryExtUzdnufbhfu9LBTw== -----END AGE ENCRYPTED FILE----- - recipient: age1pdk6dmyxqhdaja5d0nf8f9qjd43hmfahmkure5yrf8al9jyfmd8qfdxwl6 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0ajhrbkdqcmpRd2svYmNx - dHIzRDJTTTVGV3VON0NBZnpaQTdGM2w4aXpNClNqb2Y4Qzg4OWx2a3FNdG5MY1ZQ - R2dOSC9CdzBPM1I5b1lCVnptMGM3eUEKLS0tIGZRM1pSV3J0NlJvQUZBcTVNZGxy - SS9qQlJiRU1EelZySWpNdWRiTms3ZncKkwp1WT9LWxnJb+yjilikTHRm1fbs89TU - 1Xzy+GNiZbtm2I6e5XaaD+9d3PFvqqu0OdqrMrTMmiNAys0WhwB8Iw== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOZ01sMXhvMG5BMmxwa3h1 + aXArSkxUNUkwOHAzQnlkYk4vVmEzckZkaFVzCnpYRmpqMW9QM29VTWFnc0NYTURj + REp0OVNPN1lRRUdPWlQrTmhYcEZwbm8KLS0tIFI2dFBzbmQ4Q01PU1BLUXI5eXIy + Y0ZMMmJTOVRrbXpOVkYxektxOFFkZUEKcjX/wDMvuZ+PzfydOGOf938mCVcFFD/h + nGdbXAoE+cD7/rmpaF0Bpm2WqWrkiAvE/csyaWV/HNzQ6JkXp/4jtQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-03-04T21:54:28Z" - mac: ENC[AES256_GCM,data:r+2SOcW7xLHee9kL8369yB6l/Z2XdnzGkeFygSrDgcZVfBfp/fT1xeMvu5tuu8aqsUeJ7lkFD2VKiBue95XSojdlM+5YyTerqdzLyMCRbkRivC3O2xXe90B4hVqm+twE9uu74mIznAQ2e0EO9E0MMlMNBo3EwsEcYA8gyVuB5mc=,iv:plNu3BircQ+kpPaXqlNvYlLAL5V5RX/yiETs+nY1pfw=,tag:7gVuOpzx5YsHtwodIRr4TQ==,type:str] - pgp: [] + - recipient: age15vscvpe79l287h8f3hssrj2r45xy0l3ns94zfue2fxlq43cqdsxq58vq3c + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQdVV6Vy9pRmg1ZjR5c01w + L1JWWDF6U05RNnNGTjF1VEN6ZCt6SENod2xJCjA1QngrQnJXMi9BdFByT3FPSVVR + eUEyd29YQXphZ2VuTmorOWU4MjZ1TmMKLS0tIFNSZG5iV21MSHVBNkZoZW5pV09k + eS9JTWRDaVZVN2hLcVF1S2NTdk1TWGMKVnpl9T5ZycSlJmE8M8QY62kxDp5lgagF + D6hxceNiqvgGg+GwIRXoVYiVhmNAE1R9dVTyMlAfFa2pxGg7JrPj1A== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-12-02T05:42:16Z" + mac: ENC[AES256_GCM,data:95V5+/M85gUaeJD8UhkMZCcMbYEnEBIA3qFDSDd0ZoxXQkw+IXy3/C8rV9QPK/Bq/Hv3fPWbs+0CwH5GqhjfaoIlVgo3MBlk6Z1iSuw7MTy8vGOBFWpYBrObib8f9yYIZ74CrpvCHHoFtqP5cKevmHsCvgrJpbkw4gZIhZVkjLk=,iv:0/v8QzgZYc/VMei0qP1PlK4oc4b2ch5VoBEW3P3gXxU=,tag:rFTyhSixSzl1BOIhGDJo+g==,type:str] unencrypted_suffix: _unencrypted - version: 3.9.4 + version: 3.11.0 diff --git a/hosts/dango/default.nix b/hosts/dango/default.nix index 7e89de2..6b93978 100644 --- a/hosts/dango/default.nix +++ b/hosts/dango/default.nix @@ -91,5 +91,11 @@ hammerspoon.enable = true; kitty.enable = true; }; + email = { + enable = true; + davmail = true; + neomutt = true; + mbsync = true; + }; }; } diff --git a/hosts/default.nix b/hosts/default.nix index e8aa007..34a7e1a 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -59,6 +59,7 @@ let imports = [ inputs.nix-index-database.homeModules.nix-index inputs.sops-nix.homeManagerModules.sops + ../modules/home-manager ../home ] ++ lib.optional (builtins.pathExists ./${host}/home.nix) ./${host}/home.nix; @@ -134,6 +135,7 @@ let inputs.nix-index-database.homeModules.nix-index inputs.sops-nix.homeManagerModules.sops inputs.mac-app-util.homeManagerModules.default + ../modules/home-manager ../home ] ++ lib.optional (builtins.pathExists ./${host}/home.nix) ./${host}/home.nix; diff --git a/hosts/pickwick/default.nix b/hosts/pickwick/default.nix index 48232f2..949e7bd 100644 --- a/hosts/pickwick/default.nix +++ b/hosts/pickwick/default.nix @@ -1,13 +1,5 @@ { - inputs, - lib, - config, - pkgs, - system, - dots, user, - hm, - host, ... }: { diff --git a/modules/home-manager/davmail.nix b/modules/home-manager/davmail.nix new file mode 100644 index 0000000..fe35487 --- /dev/null +++ b/modules/home-manager/davmail.nix @@ -0,0 +1,163 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) + getExe + mapAttrsRecursive + mkDefault + mkEnableOption + mkIf + mkMerge + mkOption + optionalAttrs + types + ; + + cfg = config.services.davmail; + + isDarwin = pkgs.stdenv.isDarwin; + isLinux = pkgs.stdenv.isLinux; + + javaProperties = pkgs.formats.javaProperties { }; + + settingsFile = javaProperties.generate "davmail.properties" cfg.settings; +in +{ + meta.maintainers = [ lib.maintainers.bmrips ]; + + options.services.davmail = { + enable = mkEnableOption "DavMail, an MS Exchange gateway."; + + package = lib.mkPackageOption pkgs "davmail" { }; + + imitateOutlook = mkOption { + type = types.bool; + default = false; + description = "Whether DavMail pretends to be Outlook."; + example = true; + }; + + settings = mkOption { + type = javaProperties.type; + default = { }; + description = '' + Davmail configuration. Refer to + + and + for details on supported values. + ''; + example = { + "davmail.url" = "https://outlook.office365.com/EWS/Exchange.asmx"; + "davmail.allowRemote" = true; + "davmail.imapPort" = 55555; + "davmail.bindAddress" = "10.0.1.2"; + "davmail.smtpSaveInSent" = true; + "davmail.folderSizeLimit" = 10; + "davmail.caldavAutoSchedule" = false; + "log4j.logger.rootLogger" = "DEBUG"; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + # Common configuration for all platforms + { + services.davmail.settings = + mapAttrsRecursive (_: mkDefault) { + "davmail.server" = true; + "davmail.disableUpdateCheck" = true; + "davmail.logFilePath" = "${config.xdg.stateHome}/davmail.log"; + "davmail.logFileSize" = "1MB"; + "davmail.mode" = "auto"; + "davmail.url" = "https://outlook.office365.com/EWS/Exchange.asmx"; + "davmail.caldavPort" = 1080; + "davmail.imapPort" = 1143; + "davmail.ldapPort" = 1389; + "davmail.popPort" = 1110; + "davmail.smtpPort" = 1025; + + "davmail.oauth.tokenFilePath" = "${config.xdg.stateHome}/davmail-tokens"; + + "log4j.logger.davmail" = "WARN"; + "log4j.logger.httpclient.wire" = "WARN"; + "log4j.logger.org.apache.commons.httpclient" = "WARN"; + "log4j.rootLogger" = "WARN"; + } + // optionalAttrs cfg.imitateOutlook { + "davmail.oauth.clientId" = "d3590ed6-52b3-4102-aeff-aad2292ab01c"; + "davmail.oauth.redirectUri" = "urn:ietf:wg:oauth:2.0:oob"; + }; + + home.packages = [ cfg.package ]; + } + + # Linux-specific: systemd user service + (mkIf isLinux { + systemd.user.services.davmail = { + Unit = { + Description = "DavMail POP/IMAP/SMTP Exchange Gateway"; + After = [ + "graphical-session.target" + "network.target" + ]; + }; + Install.WantedBy = [ "graphical-session.target" ]; + Service = { + Type = "exec"; + ExecStart = "${getExe cfg.package} ${settingsFile}"; + Restart = "on-failure"; + + CapabilityBoundingSet = [ "" ]; + DeviceAllow = [ "" ]; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectSystem = "strict"; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "@system-service"; + SystemCallErrorNumber = "EPERM"; + UMask = "0077"; + }; + }; + }) + + # Darwin-specific: launchd agent + (mkIf isDarwin { + launchd.agents.davmail = { + enable = true; + config = { + ProgramArguments = [ + "${getExe cfg.package}" + "${settingsFile}" + ]; + KeepAlive = true; + RunAtLoad = true; + StandardErrorPath = "/tmp/davmail.err.log"; + StandardOutPath = "/tmp/davmail.out.log"; + }; + }; + }) + ]); +} diff --git a/modules/home-manager/default.nix b/modules/home-manager/default.nix new file mode 100644 index 0000000..492007c --- /dev/null +++ b/modules/home-manager/default.nix @@ -0,0 +1,11 @@ +{ + disabledModules = [ + "services/davmail.nix" + "services/mbsync.nix" + ]; + + imports = [ + ./davmail.nix + ./mbsync.nix + ]; +} diff --git a/modules/home-manager/mbsync.nix b/modules/home-manager/mbsync.nix new file mode 100644 index 0000000..3a2ccb6 --- /dev/null +++ b/modules/home-manager/mbsync.nix @@ -0,0 +1,159 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) + concatStringsSep + mkIf + mkMerge + mkOption + optionalAttrs + types + ; + + cfg = config.services.mbsync; + + isDarwin = pkgs.stdenv.isDarwin; + isLinux = pkgs.stdenv.isLinux; + + mbsyncOptions = [ + "--all" + ] + ++ lib.optional cfg.verbose "--verbose" + ++ lib.optional (cfg.configFile != null) "--config ${cfg.configFile}"; + + mbsyncCommand = "${cfg.package}/bin/mbsync ${concatStringsSep " " mbsyncOptions}"; + + # Convert systemd calendar format to launchd interval (approximate) + # Format like "*:0/5" means every 5 minutes + # We'll parse simple cases, default to 5 minutes + parseFrequencyToSeconds = + freq: + let + # Try to extract minute interval from patterns like "*:0/5" or "*:*:0/30" + parts = builtins.match ".*\\*/([0-9]+).*" freq; + in + if parts != null then (lib.toInt (builtins.head parts)) * 60 else 300; + +in +{ + meta.maintainers = [ lib.maintainers.pjones ]; + + options.services.mbsync = { + enable = lib.mkEnableOption "mbsync"; + + package = lib.mkPackageOption pkgs "isync" { }; + + frequency = mkOption { + type = types.str; + default = "*:0/5"; + description = '' + How often to run mbsync. On Linux, this value is passed to the systemd + timer configuration as the onCalendar option. See + {manpage}`systemd.time(7)` for more information about the format. + On Darwin, this is converted to an approximate interval in seconds. + ''; + }; + + verbose = mkOption { + type = types.bool; + default = true; + description = '' + Whether mbsync should produce verbose output. + ''; + }; + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Optional configuration file to link to use instead of + the default file ({file}`~/.mbsyncrc`). + ''; + }; + + preExec = mkOption { + type = types.nullOr types.str; + default = null; + example = "mkdir -p %h/mail"; + description = '' + An optional command to run before mbsync executes. This is + useful for creating the directories mbsync is going to use. + ''; + }; + + postExec = mkOption { + type = types.nullOr types.str; + default = null; + example = "\${pkgs.mu}/bin/mu index"; + description = '' + An optional command to run after mbsync executes successfully. + This is useful for running mailbox indexing tools. + ''; + }; + }; + + config = mkIf cfg.enable (mkMerge [ + # Linux-specific: systemd user service and timer + (mkIf isLinux { + systemd.user.services.mbsync = { + Unit = { + Description = "mbsync mailbox synchronization"; + }; + + Service = { + Type = "oneshot"; + ExecStart = mbsyncCommand; + } + // (optionalAttrs (cfg.postExec != null) { + ExecStartPost = cfg.postExec; + }) + // (optionalAttrs (cfg.preExec != null) { + ExecStartPre = cfg.preExec; + }); + }; + + systemd.user.timers.mbsync = { + Unit = { + Description = "mbsync mailbox synchronization"; + }; + + Timer = { + OnCalendar = cfg.frequency; + Unit = "mbsync.service"; + }; + + Install = { + WantedBy = [ "timers.target" ]; + }; + }; + }) + + # Darwin-specific: launchd agent + (mkIf isDarwin { + launchd.agents.mbsync = { + enable = true; + config = + let + # Build a script that handles pre/post exec + mbsyncScript = pkgs.writeShellScript "mbsync-wrapper" '' + set -e + ${lib.optionalString (cfg.preExec != null) cfg.preExec} + ${mbsyncCommand} + ${lib.optionalString (cfg.postExec != null) cfg.postExec} + ''; + in + { + ProgramArguments = [ "${mbsyncScript}" ]; + StartInterval = parseFrequencyToSeconds cfg.frequency; + RunAtLoad = true; + StandardErrorPath = "/tmp/mbsync.err.log"; + StandardOutPath = "/tmp/mbsync.out.log"; + }; + }; + }) + ]); +}