Replace notmuch with mu4e for email

mu4e's refile moves messages between IMAP folders, enabling
cross-device sync via mbsync. notmuch tags are local-only.

- Switch from notmuch to mu/mu4e packages
- Auto-initialize mu database on first sync
- Configure mu4e with folder shortcuts and refile to Archive
- Add NixOS load-path for mu4e elisp files
This commit is contained in:
2026-01-04 12:59:35 -08:00
parent 7898def044
commit dab96a1c50
3 changed files with 43 additions and 77 deletions

View File

@@ -159,31 +159,48 @@
(dolist (module '("bbdb" "buffer" "elisp" "emacs" "gnus" "os" "search-and-replace" "url")) (dolist (module '("bbdb" "buffer" "elisp" "emacs" "gnus" "os" "search-and-replace" "url"))
(gptel-tool-library-load-module module))) (gptel-tool-library-load-module module)))
;; Notmuch email configuration ;; mu4e email configuration
(after! notmuch ;; Add NixOS mu4e to load-path (installed via mu.mu4e package)
(setq notmuch-search-oldest-first nil (when-let ((mu-path (executable-find "mu")))
notmuch-show-logo nil (add-to-list 'load-path
notmuch-fcc-dirs "proton/Sent" (expand-file-name "../share/emacs/site-lisp/mu4e"
(file-name-directory mu-path))))
;; User identity (after! mu4e
user-mail-address "john@ogle.fyi" ;; User identity
user-full-name "John Ogle" (setq user-mail-address "john@ogle.fyi"
user-full-name "John Ogle")
;; Sending mail via msmtp ;; Maildir location (no account prefix - single account)
message-send-mail-function 'message-send-mail-with-sendmail (setq mu4e-maildir "~/Mail"
mu4e-attachment-dir "~/Downloads")
;; Folder config (matches ~/Mail/INBOX, ~/Mail/Sent, etc.)
(setq mu4e-sent-folder "/Sent"
mu4e-drafts-folder "/Drafts"
mu4e-trash-folder "/Trash"
mu4e-refile-folder "/Archive")
;; Shortcuts for common folders
(setq mu4e-maildir-shortcuts
'((:maildir "/INBOX" :key ?i)
(:maildir "/Archive" :key ?a)
(:maildir "/Sent" :key ?s)
(:maildir "/Trash" :key ?t)))
;; Behavior settings
(setq mu4e-get-mail-command "mbsync -a"
mu4e-update-interval 300 ; 5 minutes (matches systemd timer)
mu4e-change-filenames-when-moving t ; required for mbsync
mu4e-headers-date-format "%Y-%m-%d"
mu4e-headers-time-format "%H:%M")
;; Sending mail via msmtp
(setq message-send-mail-function 'message-send-mail-with-sendmail
sendmail-program (executable-find "msmtp") sendmail-program (executable-find "msmtp")
message-sendmail-envelope-from 'header message-sendmail-envelope-from 'header
mail-envelope-from 'header mail-envelope-from 'header
mail-specify-envelope-from t mail-specify-envelope-from t))
;; Saved searches for quick access
notmuch-saved-searches
'((:name "inbox" :query "tag:inbox" :key "i")
(:name "unread" :query "tag:unread" :key "u")
(:name "flagged" :query "tag:flagged" :key "f")
(:name "sent" :query "tag:sent" :key "t")
(:name "drafts" :query "tag:draft" :key "d")
(:name "all" :query "*" :key "a"))))
;; Whenever you reconfigure a package, make sure to wrap your config in an ;; Whenever you reconfigure a package, make sure to wrap your config in an
;; `after!' block, otherwise Doom's defaults may override your settings. E.g. ;; `after!' block, otherwise Doom's defaults may override your settings. E.g.

View File

@@ -176,8 +176,8 @@
;;zig ; C, but simpler ;;zig ; C, but simpler
:email :email
;;(mu4e +org +gmail) (mu4e +org)
notmuch ;;notmuch
;;(wanderlust +gmail) ;;(wanderlust +gmail)
:app :app

View File

@@ -7,14 +7,15 @@ let
in in
{ {
options.home.roles.email = { options.home.roles.email = {
enable = mkEnableOption "Enable email with notmuch, mbsync, and msmtp"; enable = mkEnableOption "Enable email with mu4e, mbsync, and msmtp";
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
home.packages = with pkgs; [ home.packages = with pkgs; [
isync # provides mbsync for IMAP sync isync # provides mbsync for IMAP sync
msmtp # for SMTP sending msmtp # for SMTP sending
notmuch # email indexing and search mu # email indexer for mu4e
mu.mu4e # mu4e elisp files for Emacs
openssl # for certificate management openssl # for certificate management
]; ];
@@ -52,58 +53,6 @@ in
SyncState * SyncState *
''; '';
# Notmuch configuration
home.file.".notmuch-config".text = ''
[database]
path=${config.home.homeDirectory}/Mail
[user]
name=John Ogle
primary_email=john@ogle.fyi
[new]
tags=unread;new;
ignore=
[search]
exclude_tags=deleted;spam;
[maildir]
synchronize_flags=true
'';
# Notmuch post-new hook for folder-based tagging
home.file."Mail/.notmuch/hooks/post-new" = {
executable = true;
text = ''
#!${pkgs.bash}/bin/bash
# Tag messages based on their maildir folder location
# This runs after 'notmuch new' indexes new messages
# Process only messages with the 'new' tag (just indexed)
# Add inbox tag only for messages in INBOX folder
${pkgs.notmuch}/bin/notmuch tag +inbox -- tag:new AND folder:INBOX
# Add sent tag for messages in Sent folder
${pkgs.notmuch}/bin/notmuch tag +sent -inbox -- tag:new AND folder:Sent
# Add draft tag for messages in Drafts folder
${pkgs.notmuch}/bin/notmuch tag +draft -inbox -- tag:new AND folder:Drafts
# Add spam tag for messages in Spam folder
${pkgs.notmuch}/bin/notmuch tag +spam -inbox -unread -- tag:new AND folder:Spam
# Add deleted tag for messages in Trash folder
${pkgs.notmuch}/bin/notmuch tag +deleted -inbox -unread -- tag:new AND folder:Trash
# Add archive tag for messages in Archive folder
${pkgs.notmuch}/bin/notmuch tag +archive -inbox -- tag:new AND folder:Archive
# Remove the temporary 'new' tag from all messages
${pkgs.notmuch}/bin/notmuch tag -new -- tag:new
'';
};
# msmtp configuration # msmtp configuration
home.file.".msmtprc".text = '' home.file.".msmtprc".text = ''
# Default settings # Default settings
@@ -135,7 +84,7 @@ in
}; };
Service = { Service = {
Type = "oneshot"; Type = "oneshot";
ExecStart = "${pkgs.bash}/bin/bash -c '${pkgs.isync}/bin/mbsync -a && ${pkgs.notmuch}/bin/notmuch new'"; ExecStart = "${pkgs.bash}/bin/bash -c '${pkgs.isync}/bin/mbsync -a && (${pkgs.mu}/bin/mu info >/dev/null 2>&1 || ${pkgs.mu}/bin/mu init --maildir ~/Mail) && ${pkgs.mu}/bin/mu index'";
Environment = "PATH=${pkgs.rbw}/bin:${pkgs.coreutils}/bin"; Environment = "PATH=${pkgs.rbw}/bin:${pkgs.coreutils}/bin";
StandardOutput = "journal"; StandardOutput = "journal";
StandardError = "journal"; StandardError = "journal";