{"id":27063882,"url":"https://github.com/danielfleischer/mu4easy","last_synced_at":"2026-01-19T13:01:38.501Z","repository":{"id":129052363,"uuid":"366093521","full_name":"danielfleischer/mu4easy","owner":"danielfleischer","description":"mu4e + mbsync configuration for multiple email accounts in Emacs.","archived":false,"fork":false,"pushed_at":"2025-06-21T12:42:42.000Z","size":125,"stargazers_count":63,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-10-11T22:16:37.573Z","etag":null,"topics":["emacs-lisp","email","mu4e"],"latest_commit_sha":null,"homepage":"","language":"Emacs Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielfleischer.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-10T15:39:13.000Z","updated_at":"2025-09-03T16:30:23.000Z","dependencies_parsed_at":"2023-12-08T09:39:36.759Z","dependency_job_id":null,"html_url":"https://github.com/danielfleischer/mu4easy","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/danielfleischer/mu4easy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfleischer%2Fmu4easy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfleischer%2Fmu4easy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfleischer%2Fmu4easy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfleischer%2Fmu4easy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielfleischer","download_url":"https://codeload.github.com/danielfleischer/mu4easy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielfleischer%2Fmu4easy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28568833,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T12:50:50.164Z","status":"ssl_error","status_checked_at":"2026-01-19T12:50:42.704Z","response_time":67,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["emacs-lisp","email","mu4e"],"created_at":"2025-04-05T16:35:21.903Z","updated_at":"2026-01-19T13:01:38.494Z","avatar_url":"https://github.com/danielfleischer.png","language":"Emacs Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"#+TITLE: mu4easy\n\n#+html: \u003ca href=\"https://www.gnu.org/software/emacs/\"\u003e\u003cimg alt=\"GNU Emacs\" src=\"https://github.com/minad/corfu/blob/screenshots/emacs.svg?raw=true\"/\u003e\u003c/a\u003e\n#+html: \u003ca href=\"https://melpa.org/#/mu4easy\"\u003e\u003cimg alt=\"MELPA\" src=\"https://melpa.org/packages/mu4easy-badge.svg\"/\u003e\u003c/a\u003e\n#+html: \u003ca href=\"https://stable.melpa.org/#/mu4easy\"\u003e\u003cimg src=\"https://stable.melpa.org/packages/mu4easy-badge.svg\"\u003e\u003c/a\u003e\n\nA global minor mode that defines a full working setup for mu4e and =mbsync=, based on mu 1.12+. Easily setup accounts and aliases from these providers: *Google*, *Apple*, *GMX*, *Outlook*, and *Proton*. Additional packages are installed and configured for a better experience, including =mu4e-column-faces=, =mu4e-alert=, and =org-msg=. Some customizations are available, run =(customize-group 'mu4easy)=.\n\nInstall locally and use the =load-path= variable, e.g.:\n#+begin_src elisp\n(use-package mu4easy\n  :load-path \"~/Code/mu4easy\"\n  :bind (\"C-c u\" . mu4e)\n  :config (mu4easy-mode))\n#+end_src\n\nOr install with Melpa. Here is an example with one email account:\n#+begin_src elisp\n(use-package mu4easy\n  :ensure t\n  :bind (\"C-c u\" . mu4e)\n  :config (mu4easy-mode)\n  :custom\n  (mu4easy-contexts '((mu4easy-context\n                       :c-name  \"Google\"\n                       :maildir \"Gmail\"\n                       :mail    \"a@gmail.com\"\n                       :smtp    \"smtp.gmail.com\"\n                       :sent-action delete))))\n#+end_src\nSee later more details on the =mu4easy-context= macro.\n\nMake sure you have =mu= in your in PATH.\n\n- =mbsync= Configurations:\n   - [[#Google][Google]]\n   - [[#Apple][Apple]]\n   - [[#GMX][GMX]]\n   - [[#Outlook][Outlook]]\n   - [[#Proton][Proton]]\n- [[#mu4e][mu4e configuration]]\n\n*Comments and suggestions are welcome*.\n\n** mbsync\n:PROPERTIES:\n:CREATED:  [2021-05-09 Sun 22:39]\n:END:\n\nThe important part is to normalize all accounts to have the same structure, containing the folders Inbox, Archive, Sent, Trash, Drafts and Spam. I would advice against syncing the Drafts folder because it can lead to issues. If you decide not to sync it, just remove the relevant lines from the =.mbsyncrc= file. *We assume isync 1.5.0 or newer*.\n\nLet's go over each provider and examine the gotchas.\n\n*** Google\n:PROPERTIES:\n:CREATED:  [2021-05-09 Sun 22:42]\n:END:\n\n#+begin_src conf\nIMAPAccount Gmail\nHost imap.gmail.com\nUser a@gmail.com\nPassCmd \"gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine imap.gmail.com login a@gmail.com/ {print $NF}'\"\nAuthMechs LOGIN\nTLSType IMAPS\nTLSVersions +1.2\nCertificateFile /usr/local/etc/openssl@1.1/cert.pem\n\nMaildirStore Gmail-local\nPath ~/Mail/Gmail/\nInbox ~/Mail/Gmail/Inbox\nSubFolders Verbatim\n\nIMAPStore Gmail-remote\nAccount Gmail\n\nChannel Gmail-inbox\nFar :Gmail-remote:\"INBOX\"\nNear :Gmail-local:\"INBOX\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n\nChannel Gmail-trash\nFar :Gmail-remote:\"[Gmail]/Trash\"\nNear :Gmail-local:\"Trash\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n\nChannel Gmail-spam\nFar :Gmail-remote:\"[Gmail]/Spam\"\nNear :Gmail-local:\"Spam\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n\nChannel Gmail-all\nFar :Gmail-remote:\"[Gmail]/All Mail\"\nNear :Gmail-local:\"Archive\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n\nChannel Gmail-drafts\nFar :Gmail-remote:\"[Gmail]/Drafts\"\nNear :Gmail-local:\"Drafts\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n\nGroup Gmail\nChannel Gmail-inbox\nChannel Gmail-trash\nChannel Gmail-all\nChannel Gmail-spam\nChannel Gmail-drafts\n#+end_src\n\nFirst is the =PassCmd=. I assume all passwords (and application specific passwords) are in =~/.authinfo.gpg= with the password key-value pair last (see the =NF= variable in =awk= pointing to the last column).\n\nNext, Google has the structure =[Gmail]/...= so we use explicit far/near definitions. We'll drop them in the next providers.\n\nFinally, unlike other providers, we're not going to sync the Sent folders because Google is saving all email in the All Mails folders and you'll end up with duplicates locally.\n\n*** Apple\n:PROPERTIES:\n:CREATED:  [2021-05-09 Sun 22:46]\n:END:\n#+begin_src conf\nIMAPAccount Apple\nHost imap.mail.me.com\nPORT 993\nUser a@icloud.com\nPassCmd \"gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine imap.mail.me.com/ {print $NF}'\"\nAuthMechs LOGIN\nTLSType IMAPS\nTLSVersions +1.2\nCertificateFile /usr/local/etc/openssl@1.1/cert.pem\n\nMaildirStore Apple-local\nPath ~/Mail/Apple/\nInbox ~/Mail/Apple/Inbox\nSubFolders Verbatim\n\nIMAPStore Apple-remote\nAccount Apple\n\nChannel Apple-all\nFar :Apple-remote:\nNear :Apple-local:\nPatterns \"INBOX\" \"Archive\" \"Trash\" \"Spam\" \"Drafts\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n\nChannel Apple-sent\nFar :Apple-remote:\"Sent Messages\"\nNear :Apple-local:\"Sent\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n\nGroup Apple\nChannel Apple-sent\nChannel Apple-all\n#+end_src\n\nHere we use the =Patterns= key to quickly select the folders we're interested in. It turns the Sent folders has many conventions; at Apple it's called Sent Messages.\n\n*** GMX\n:PROPERTIES:\n:CREATED:  [2021-05-09 Sun 22:48]\n:END:\n\n#+begin_src conf\nIMAPAccount GMX\nHost imap.gmx.com\nUser a@gmx.com\nPassCmd \"gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine imap.gmx.com login a@gmx.com/ {print $NF}'\"\nAuthMechs LOGIN\nTLSType IMAPS\nTLSVersions +1.2\nCertificateFile /usr/local/etc/openssl@1.1/cert.pem\n\nMaildirStore GMX-local\nPath ~/Mail/GMX/\nInbox ~/Mail/GMX/Inbox\nSubFolders Verbatim\n\nIMAPStore GMX-remote\nAccount GMX\n\nChannel GMX\nFar :GMX-remote:\nNear :GMX-local:\nPatterns \"INBOX\" \"Archive\" \"Trash\" \"Spam\" \"Drafts\" \"Sent\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n#+end_src\n\n*** Outlook\n:PROPERTIES:\n:CREATED:  [2024-08-30 Fri 15:43]\n:END:\n#+begin_src conf\nIMAPAccount Outlook\nHost outlook.office365.com\nUser a@outlook.com\nPassCmd \"gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine outlook.office365.com login a@outlook.com/ {print $NF}'\"\nAuthMechs LOGIN\nTLSType IMAPS\nTLSVersions +1.2\n\nMaildirStore Outlook-local\nPath ~/Mail/Outlook/\nInbox ~/Mail/Outlook/Inbox\nSubFolders Verbatim\n\nIMAPStore Outlook-remote\nAccount Outlook\n\nChannel Outlook-all\nFar :Outlook-remote:\nNear :Outlook-local:\nPatterns \"INBOX\" \"Archive\" \"Sent\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n\nChannel Outlook-junk\nFar :Outlook-remote:\"Junk\"\nNear :Outlook-local:\"Spam\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n\nChannel Outlook-trash\nFar :Outlook-remote:\"Deleted\"\nNear :Outlook-local:\"Trash\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n\nGroup Outlook\nChannel Outlook-all\nChannel Outlook-junk\nChannel Outlook-trash\n#+end_src\n*** Proton\n:PROPERTIES:\n:CREATED:  [2021-05-09 Sun 22:49]\n:END:\n\n#+begin_src conf\nIMAPAccount Proton\nHost 127.0.0.1\nPORT 1111\nUser a@protonmail.com\nPassCmd \"gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg | awk '/machine 127.0.0.1/ {print $NF}'\"\nAuthMechs LOGIN\nTLSType STARTTLS\nTLSVersions +1.2\nCertificateFile /usr/local/etc/openssl@1.1/cert.pem\n\nMaildirStore Proton-local\nPath ~/Mail/Proton/\nInbox ~/Mail/Proton/Inbox\nSubFolders Verbatim\n\nIMAPStore Proton-remote\nAccount Proton\n\nChannel Proton\nFar :Proton-remote:\nNear :Proton-local:\nPatterns \"INBOX\" \"Archive\" \"Trash\" \"Spam\" \"Drafts\" \"Sent\"\nCopyArrivalDate yes\nCreate Both\nExpunge Both\nSyncState *\n#+end_src\n\nIn order to us Proton, one needs to install a bridge application. It specifies the IMAP and SMTP ports to use (non-standard).\n\n** mu4e\n:PROPERTIES:\n:CREATED:  [2021-05-09 Sun 22:53]\n:END:\n\nLet's go over the important parts of the elisp code.\n\n#+begin_src elisp\n(setf (alist-get 'refile mu4e-marks)\n      '(:char (\"r\" . \"▶\")\n              :prompt \"refile\"\n              :dyn-target (lambda (target msg) (mu4e-get-refile-folder msg))\n              ;; Notice the special treatment for Gmail.\n              :action (lambda (docid msg target)\n                        (let ((maildir (mu4e-message-field msg :maildir)))\n                          (if (string-match-p \"Gmail\\\\|Google\" maildir)\n                              (mu4e--server-remove docid)\n                            (mu4e--server-move docid (mu4e--mark-check-target target) \"+S-u-N\"))))))\n#+end_src\n\nGmail requires special treatment for *archiving* and *deletion*, since folders are labels and everything exist in the =All Mail=, in addition to other \"locations\" like =Inbox=, =Sent=, etc. Here, we deal with Gmail by regex matching with the maildir name; adapt if needed. In this example, archiving for Gmail means removing the email (from the Inbox) while for other providers it means moving the email. Similar notion for deletion.\n\n#+begin_src elisp\n(cl-defmacro mu4easy-context (\u0026key c-name maildir mail smtp\n                                   (smtp-mail mail)\n                                   (smtp-port 587)\n                                   (smtp-type 'starttls)\n                                   (sent-action 'sent)\n                                   (name \"Daniel Fleischer\")\n                                   (sig \"Daniel Fleischer\"))\n  (let\n      ((inbox      (concat \"/\" maildir \"/Inbox\"))\n       (sent       (concat \"/\" maildir \"/Sent\"))\n       (trash      (concat \"/\" maildir \"/Trash\"))\n       (refile     (concat \"/\" maildir \"/Archive\"))\n       (draft      (concat \"/\" maildir \"/Drafts\")))\n\n    `(make-mu4e-context\n      :name ,c-name\n      :match-func (lambda (msg)\n                    (when msg\n                      (string-match-p ,mail (plist-get (car (mu4e-message-field msg :to)) :email))))\n      :vars '((user-mail-address . ,mail)\n              (user-full-name . ,name)\n              (mu4e-sent-folder . ,sent)\n              (mu4e-drafts-folder . ,draft)\n              (mu4e-trash-folder . ,trash)\n              (mu4e-refile-folder . ,refile)\n              (mu4e-sent-messages-behavior . ,sent-action)\n              (smtpmail-stream-type . ,smtp-type)\n              (smtpmail-smtp-service . ,smtp-port)\n              (smtpmail-smtp-user . ,smtp-mail)\n              (smtpmail-smtp-server . ,smtp)\n              (smtpmail-debug-info . t)\n              (smtpmail-debug-verbose . t)\n              (org-msg-signature . ,sig)\n              (mu4e-maildir-shortcuts .\n                                      ((,inbox   . ?i)\n                                       (,sent    . ?s)\n                                       (,trash   . ?t)\n                                       (,refile  . ?a)\n                                       (,draft   . ?d)))))))\n#+end_src\n\nThat's the macro to create contexts or identities. It's assuming you have a consistent maildirs structure, like specified in the =mbsync= config, i.e. all account names are on a single level and then below them you have Inbox, Archive, Trash, Sent, Spam and Drafts for each one. To match the context I'm just looking at the maildir the message is in. Some defaults in the function are the SMTP encryption and what to do with sent messages (either delete them in the case of Google or save them in the Sent folder; more on that in the contexts examples).\n\nNext there are some variables settings; these are set to taste, feel free to experiment with them. Next are the bookmarks, which are very convenient both for jumping and for reading the read/unread counts.\n\n*Tip*: the bookmarks query can be either a function or a string. If it's a function, there is no read/unread count. I'm using a string generated from a function; if you first eval the string and then set the variable, you do get counts.\n\nFor *org-msg* package users, notice that the package itself handles the signature, so you want to define ~org-msg-signature~ like I did in the macro. It accepts =org= formatting, e.g. ~*Daniel Fleischer*\\n/Skynet Inc/~ and then converts it into formatted HTML. Also, when using *org-msg*, reply style is /top-posting/ so you need it to handle the signature correctly (above the replied text).\n\nAnother improvement is creating a customized link description; i.e. calling ~org-store-link~ to save a link to an email, it uses ~mu4easy-mail-link-description~ which will give a nice description of the form =to/from: subject (ISO timestamp)= - works with =org-capture= as well.\n\nAdded is a custom updating function that asks you which account to update, or by default updates all. It is bound to the usual \"U\".\n\nFinally, setting up the accounts, either using customization or using code:\n#+begin_src elisp\n(setq mu4easy-contexts\n\n      '((mu4easy-context\n         :c-name  \"Google\"\n         :maildir \"Gmail\"\n         :mail    \"a@gmail.com\"\n         :smtp    \"smtp.gmail.com\"\n         :sent-action delete)\n\n        (mu4easy-context\n         :c-name  \"1-GMX\"\n         :maildir \"GMX\"\n         :mail    \"a@gmx.com\"\n         :smtp    \"mail.gmx.com\")\n\n        (mu4easy-context\n         :c-name    \"2-GMX-alias\"\n         :maildir   \"GMX\"\n         :mail      \"a.alias@gmx.com\"\n         :smtp      \"mail.gmx.com\"\n         :smtp-mail \"a@gmx.com\")\n\n        (mu4easy-context\n         :c-name  \"Apple\"\n         :maildir \"Apple\"\n         :mail    \"a@icloud.com\"\n         :smtp    \"smtp.mail.me.com\")\n\n        (mu4easy-context\n         :c-name  \"3-Apple-alias\"\n         :maildir \"Apple\"\n         :mail    \"a@me.com\"\n         :smtp    \"smtp.mail.me.com\"\n         :smtp-mail \"a@icloud.com\")\n\n        (mu4easy-context\n         :c-name  \"Outlook\"\n         :maildir \"Outlook\"\n         :mail    \"a@outlook.com\"\n         :smtp    \"smtp-mail.outlook.com\")\n\n        (mu4easy-context\n         :c-name    \"Proton\"\n         :maildir   \"Proton\"\n         :mail      \"a@protonmail.com\"\n         :smtp      \"127.0.0.1\"\n         :smtp-type ssl\n         :smtp-port 999)\n\n        (mu4easy-context\n         :c-name    \"4-Proton-alias\"\n         :maildir   \"Proton\"\n         :mail      \"a@pm.com\"\n         :smtp      \"127.0.0.1\"\n         :smtp-mail \"a@protonmail.com\"\n         :smtp-type ssl\n         :smtp-port 999)))\n#+end_src\n\n_Important points:_\n1. Jumping to contexts is based on their first (unique) letter, that's why I'm using numbers in the =c-name= key.\n2. Google saves the sent messages in the All Mail (Archive) folder so it is recommended to set the corresponding mu4e setting to delete sent messages (locally). It's only for Google; for the other account, sent messages are saved in the Sent folder.\n3. The =2-GMX= account is an alias - not another GMX account (see the last comment). It has a different mail, but the SMTP authentication needs the real email address. The outgoing email still looks like it is coming from the alias.\n4. Proton account needs SSL encryption for SMTP, it connects to =localhost= and uses non-standard ports for IMAP and SMTP (check the Proton bridge app for details).\n5. If you have multiple accounts with the same providers, they should have different maildirs, e.g. =~/Mail/Gmail1/=, =~/Mail/Gmail2/=. Here I showed aliases, not multiple accounts.\n\n** Disclaimer\n:PROPERTIES:\n:CREATED:  [2021-05-09 Sun 22:32]\n:ID:       2AECA758-B861-446B-B73E-C34DDF6EBD3F\n:END:\n\nThis setup is based upon a couple of weeks worth of tweaking, trial and error. It's not perfect; some email were lost (those not saved into =sent=), lessons were learned. I didn't try it with Microsoft-based emails. Make sure to test everything you do - to see that emails are going in and out, saved in =Archive= and =Sent=, compare the results with the web-based interface until you get comfortable using it 100% of the time. Or not.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielfleischer%2Fmu4easy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielfleischer%2Fmu4easy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielfleischer%2Fmu4easy/lists"}