I have found a good offline terminal-based email solution which I am happy with. My requirements were as follows:
- Emails stored on my hard drive in a sensible format
- Synchronisation of read and deleted status
- Terminal client for reading/browsing email
- Multiple email accounts all appear in one list
Limitation: I have not yet come up with a solution for sending mail from multiple accounts with my current approach. I don't send much email, so I just do that from the web UI when I need to.
In my search for a solution I tried mutt, NeoMutt, sup, and aerc all for a period each. The only email client which actually offered all the features I was after straight out of the box was sup, so it's probably worth you checking that out.
I stuck with sup for a while, but eventually gave up on it. It crashed a lot, required various ruby packages to be installed, required pinning certain versions, and various other fixing. One day it stopped displaying my mail entirely, and I decided I couldn't really be bothered to fix it again, so I started my search back up.
I believe I have discovered my perfect solution using a combination of notmuch and NeoMutt. NeoMutt is rather a pain to configure and not obvious in any way in my view, so I'm sure there's a lot more configuration I could do to make it better, but I have the bones of what works, which I will share
ProtonMail provides a couple of synchronisation challenges on it's own, due to the level of IMAP feature support offered by ProtonMail Bridge. I will cover that too.
ProtonMail and mbsync
Install ProtonMail Bridge and configure it with your mail accounts.
Install mbsync/isync. This will be used to synchronise all your mail into files in the Maildir format on your local machine.
You need to configure your ~/.mbsyncrc. Here is where there are some limitations
with ProtonMail.
ProtonMail provides you with two stores that you can use to read your inbox: 'All Mail' or 'INBOX' (these can be viewed within the browser as well).
I first tried syncing 'All Mail' so that everything would be synced even if it's in a folder on the remote. This has a problem however, and it is to do with deletion. ProtonMail Bridge does not appear to support the deletion of mail from the 'All Mail' folder. If you delete something on your local machine, mbsync will give you error messages when you try and sync it back.
You need to sync 'INBOX' instead, however this has different weird behaviour with deletion. The deletion will propogate, but the mail will remain in the 'All Mail' folder. It won't go to the 'Trash'. This leaves your deleted mail in a weird state where it is still in your 'All Mail', but you have no way to filter or search for it, even from the web UI.
The solution is to sync 'INBOX', and also sync the 'Trash' folder separately. Then we can configure mbsync to move emails to the trash folder when deleted, so they end up in the 'Trash' on the remote side.
There appears to be no way to permanently delete email from the remote via the bridge.
This solution also requires that your email client be configured to handle the 'deletion' of emails in the correct way. What won't work is if your email client just deletes the file locally, as we need the 'deleted' email to be moved to the trash folder. The solution for this is to have a mail client that sets the MailDir 'T' (Trashed) flag on the mail, and then let mbsync take it from there.
# The local mail store
MaildirStore protonmail-personal-local
# Put it wherever you like
Path ~/mail/protonmail-personal/
# The inbox folder needs to be absolute, for some reason
Inbox ~/mail/protonmail-personal/inbox
# Tell mbsync where to put trashed emails after expunging
Trash trash
# The IMAP details for ProtonMail Bridge
IMAPAccount protonmail-personal-imap
Host 127.0.0.1
Port 1143
User personal@protonmail.com
PassCmd "/usr/bin/pass protonmail-personal"
SSLType STARTTLS
CertificateFile /etc/ssl/certs/protonmail-bridge.pem
# Specify that a store exists on the protonmail IMAP account
IMAPStore protonmail-personal-remote
Account protonmail-personal-imap
# Configure the local-remote inbox connection
Channel protonmail-personal-inbox
# Specify the remote INBOX folder
Master :protonmail-personal-remote:INBOX
# Specify the local folder, the 'Inbox' will be used by default
Slave :protonmail-personal-local:
# We want the mail gone from the local and remote inboxes
# It will be moved to the 'Trash' folder configured above
Expunge Both
# Configure the local-remote trash connection
Channel protonmail-personal-trash
Master :protonmail-personal-remote:Trash
Slave :protonmail-personal-local:trash
# Make sure you don't expunge here, as mbsync will just expunge
# straight back into the trash folder!
# Configure the local-remote sent connection
Channel protonmail-personal-sent
Master :protonmail-personal-remote:Sent
Slave :protonmail-personal-local:sent
Expunge Both
Any other ProtonMail accounts you have should be configured in the same way.
Systemd service/timer
Create a systemd service and timer to sync your mail periodically.
~/.config/systemd/user/mbsync.service
[Unit]
Description=mbsync
[Service]
Type=oneshot
ExecStart=/usr/bin/mbsync -Va
~/.config/systemd/user/mbsync.timer
[Unit]
Description=mbsync timer
[Timer]
OnBootSec=1m
OnUnitActiveSec=15m
Unit=mbsync.service
[Install]
WantedBy=timers.target
Enable and start them with systemctl --user enable --now mbsync.timer.
Notmuch
This step is optional if you don't care about having multiple accounts visible together.
The limitation with NeoMutt by default is that it just displays one MailDir at a time, and doesn't provide a function to merge them into a single view (not that I know of anyway). The solution is to use notmuch to index your mail. NeoMutt has support for using notmuch as an indexer.
You basically just need to install notmuch and run through their Getting Started guide. Index all the accounts you want and play around with the tags and stuff.
New emails need to be tagged as 'inbox' after being downloaded by mbsync. Modify your
mbsync.service to add the following
ExecStart=/usr/bin/mbsync -Va
ExecStartPost=/usr/bin/notmuch new
NeoMutt
NeoMutt can be a pain to configure, but once you work out what to do it doesn't require too many configuration options to get the bare bones working. Especially when you're already syncing with mbsync and indexing with notmuch, so we can bypass all of NeoMutt's features in those areas.
Create your ~/.config/neomutt/neomuttrc.
# Not actually relevant in this case, but NeoMutt requires it to be set
set folder = ~/mail
# Specify a notmuch query to use as your NeoMutt 'Inbox'
# Include the sent tag if you want NeoMutt's threading feature
virtual-mailboxes "inbox" "notmuch://?query=tag:inbox or tag:sent"
# Limit the notmuch query to the last 30 days
# Without this, neomutt's startup time will grow as the size of your mailbox does
set nm_query_window_duration = 30
set nm_query_window_timebase = day
# Specify that the mail should be read from the above virtual mailbox
set spoolfile = inbox
# Set the name to send from
set realname = "Benjamin Higgins"
# You have to pick one email address as default
set from = "personal@protonmail.com"
# Specify other email addresses to consider as yours
# This affects replies and threading highlighting
# Regex is supported, useful for plus addressing
alternates "personal+?.*@protonmail.com" "work@protonmail.com"
# Specify that you want to reply from the address received to
set reverse_name = yes
# Allow headers to be edited so you can edit from address
set edit_headers = yes
# Specify that deletion shouldn't actually delete the file,
# just set the 'T' (Trashed) flag on it; mbsync will handle the rest
set maildir_trash = yes
# --- OPTIONAL ---
# Sort first by thread, then by date
set sort = threads
set sort_aux = reverse-last-date-received
# Prevent scrolling up/down opening the next email
set pager_stop = yes # Prevent scrolling down opening the next email
# HTML mail handling, see https://wiki.archlinux.org/title/mutt#Viewing_HTML
set mailcap_path = ~/.config/neomutt/mailcap
auto_view text/html # Auto convert HTML emails
# Set the column format in the index view
set index_format = "%2C %Z %d %-15.15F %s (%-4.4c)"
set date_format = "%m/%d %I:%M"
At this point you should have a basic barebones setup for reading mail from multiple accounts offline in NeoMutt, with threading view.
Next steps
You may wish to have notmuch tag your sent mail as such. This doesn't affect NeoMutt
integration, but it useful if you want to use notmuch's more complex indexing features.
You could add this as another ExecStartPost in your mbsync.service.
notmuch tag -new -inbox +sent -- from:"/personal\+?.*@protonmail.com/" or from:work@protonmail.com
You can configure keybinds and colourschemes in Mutt. I use the Vi
keybinds. You may need to hunt around for the
vim-keys.rc file on your system; mine was at a. I like the gruvbox
colourscheme.
Sending mail
I very rarely send mail, so I haven't made it a priority to figure out how to do this properly. It isn't hard to get a simple setup for sending from one account with NeoMutt and msmtp, but different accounts per email address becomes a little more complex. At the moment I just log into the browser on the few occasions I need to actually send email!