Daily notes for 2024-01-09 (mutt noodling edition)
Multi-account, GPG-secured mutt config
I keep having to reinvent this every few years, and I always stitch it together from assorted sources, mostly because Google sort of shifts around now and then. So:
- Given a Gmail account with IMAP access turned on
- Given a Fastmail account using IMAP
- Given mutt, with your configuration in
~/.mutt
and withmuttrc
andmacros
files. - Given a working gpg config you can use to encrypt/decrypt
There are all sorts of ways to handle mutt config for assorted providers. The examples here are working right now, in early 2024. They probably have bits of cruft and lint because my config has been a work in progress since some time in the late 20th century.
Overview
You’re making profiles to do this: One for each of your accounts that will hold account specific config information. If you currently have a monolith config in mutt, you can lift a lot of stuff out of it and move it into a profile, then source the profile in your main muttrc
.
You’re also going to make and encrypt a credential file for each account. Some people do this all in one file and use account hooks to make sure imap_user
, imap_password
and smtp_password
are set correcctly depending on the account you’re operating in. I chose to make a file for each account.
You’re going to make macros that source the profiles when you want to switch between them.
0. Pre-config with Gmail and Fastmail
I’m not going to go into a ton of detail here:
- Gmail needs to have less secure app access turned on. Find it in your account settings. If you’re doing this for a work account, it may be your admin hasn’t enabled this. Have fun fighting city hall, in that case.
- If you have a GSuite admin, they need to have enabled all IMAP clients, not just OAuth ones.
- If you have 2FA turned on with Google, you will need to enable an application password.
For Fastmail:
- You need to have an app password set up for mutt.
Settings -> Privacy and Security -> Integrations -> App passwords
1. The profile files
Make profile files for each of your accounts. I name them workplace.profile
, fastmail.profile
, etc. It doesn’t matter there’s no required convention. It’s a good idea to use the first one as the template for the second one.
This is an example of my Fastmail profile. Note line 6:
source "gpg -d ~/.mutt/passwords.gpg |"
That’s where your credentials will come from. I’ll show that file next.
1# -*- muttrc -*-
2# Mutt sender profile : personal/default
3
4unset folder
5set smtp_authenticators = 'gssapi:login' # fastmail needs this
6set imap_authenticators = ''
7source "gpg -d ~/.mutt/passwords.gpg |"
8set spoolfile = "imaps://imap.fastmail.com"
9set folder = "imaps://imap.fastmail.com/INBOX"
10set postponed="+Drafts"
11set hostname="yourdomain.com"
12set signature= "~/.mutt/personal.sig"
13set from= "Bob Jones <[email protected]>"
14set realname = "Bob Jones"
15set smtp_url = "smtps://[email protected]@smtp.fastmail.com:465" # use your fastmail username, not your email address
16set imap_user = "[email protected]" # use your fastmail username here, too
17
18# set the status to show which profile I'm using
19set status_format= "-%r-Fastmail: %f [Msgs:%?M?%M/?%m%?n? New:%n?%?o? Old:%o?%?d? Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b?%?l? %l?]---(%s/%S)-%>-(%P)---\n"
20
21unmy_hdr *
22
23my_hdr From: Bob Jones <[email protected]>
24my_hdr Organization: yourdomain.com
25my_hdr Sender: Bob Jones <[email protected]>
26my_hdr Return-Path: <[email protected]>
27
28# clear the existing mailboxes list
29unmailboxes *
30
31# load up mailboxes appropriate to this profile
32mailboxes + "=Spam"
33mailboxes + "=disposable"
34mailboxes + "=Newsletters"
35mailboxes + "=Sent"
36mailboxes + "=Archive"
2. Make credentials files
For each account, you need to make a file for your credentials.
set [email protected]
set imap_pass="klatu barada nikto"
set smtp_pass="klatu barada nikto"
Name it whatever. passwords-accountname
works.
Once you’ve created the file, encrypt it with gpg:
gpg -r [email protected] -e passwords-fastmail
Test it:
gpg -d passwords-fastmail.gpg
Then shred the plaintext original:
shred -u passwords-fastmail
Make sure that your profile (from the previous step) is sourcing the gpg file in line 6 of my example, e.g.
source "gpg -d ~/.mutt/passwords-fastmail.gpg |"
3. Do a quick mid-config check
Might as well test it now. You can do that by sourcing one of your profiles in your muttrc
:
source ~/.mutt/fastmail.profile
When you run mutt the first time in this login session, you should get a gpg prompt for your credentials so mutt can decrypt your password file and use it to log in.
If it’s working, now’s the time to make your second profile and credentials files using the above steps since it’ll be good to know what they’re all called for the next step, which is making macros.
4. Make macros
I keep my macros in their own file under ~/.mutt
just to keep things modular. You can put these in your main muttrc
. Whatever you prefer. If you have a separate file, make sure to source it in muttrc
:
source ~/.mutt/macros
Now add something like this for each account:
macro index .cf '<sync-mailbox><enter-command>source ~/.mutt/fastmail.profile<enter><change-folder>!<enter>'
macro index .cg '<sync-mailbox><enter-command>source ~/.mutt/google.profile<enter><change-folder>!<enter>'
That just does one last sync, then sources your profile, then changes folders to the inbox of that profile.
Restart mutt. From the index, if all is working correctly, the macro .cf
will source your fastmail.profile
and the macro .cg
will source your google.profile
file (both of which also source/decrypt their respective credential files).
5. In conclusion
Once it’s all wired up and running, you should be able to switch back and forth between accounts with just a few seconds of latency as the inbox syncs on exit and the new inbox syncs on login.
The pleasures of mutt
I went on a mutt revival kick early last year. It remains a land of contrasts. I never end up sticking to it 100 percent of the time but instead prefer to use it as a quick triage tool: It’s easy to make macros and keybindings that speed up inbox processing. Sometimes it’s easier to just bail out to the web mail interface, but during the day it’s helpful to just burn through the inbox never taking my hands off the keyboard.
mutt scoring and color treatments
One last thing, I guess, since I’m documenting stuff. One of the reasons I like mutt for triage so much is my ability to add a little visual treatment to messages based on their scores. That makes it easy to see what in my inbox has more priority.
I’ve got this little script in my ~/.mutt
:
1#!/usr/bin/env ruby
2
3require 'mail'
4require 'tempfile'
5
6# Wants a +/- integer, e.g. +20
7score = ARGV.first
8
9score_file = "#{Dir.home}/.mutt/scored"
10
11msg = Tempfile.new('msg')
12
13msg.write($stdin.read)
14
15mail = Mail.read(msg)
16
17from = mail.from.first
18
19File.open(score_file, "a") { |f| f.write "score ~f#{from} #{score}\n"}
20
21msg.close
22
23msg.unlink
And I’ve got these macros in my ~/.mutt/macros
file:
1# Score messages
2macro index,browser .sp "<pipe-entry>~/.mutt/mailscore.rb +5\n<enter-command>source ~/.mutt/scored<enter>" # score sender +5
3macro index,browser .sP "<pipe-entry>~/.mutt/mailscore.rb +20\n<enter-command>source ~/.mutt/scored<enter>" # score sender +20
4macro index,browser .sm "<pipe-entry>~/.mutt/mailscore.rb -5\n<enter-command>source ~/.mutt/scored<enter>" # score sender -5
5macro index,browser .sM "<pipe-entry>~/.mutt/mailscore.rb -20\n<enter-command>source ~/.mutt/scored<enter>" # score sender -20
And I’ve got a few lines in my ~/.mutt/colors
file:
1color index cyan default "~n 0-2 !~p"
2color index magenta default "~n <5"
3color index brightyellow default "~n >15"
4color index brightred default "~n >19"
5#
The macros pipe a given message into the script, the script extracts the sender, and the script writes a line into my ~/.mutt/scored
file. Then the ~/.mutt/colors
file (which you need to source in muttrc
) assigns colors to certain scores. I have a few other rules in ~/.mutt/scores
, as well:
1# Date-based scoring penalties -- older things fall down
2score ~d>3d -1
3score ~d>7d -3
4score ~d>14d -10
5
6score "~O" +10 # old = +10 so I don't miss it
7score "~F" +20 # flagged = +20 so it stays in the interesting view for a while, even if old
8score "!~p ~d>7d" -10 # not for me directly, getting old, let it fade away
9score "!~l" +2 # to a known list, give it a bump