Creating an Emacs package: ‘password-menu’

TL;DR

This post documents the development journey and some implementation details of a relatively simple Emacs package and is arguably overkill for such a small piece of functionality. On the other hand, some of these details may be useful for others who want to develop similar functionality.

If you're not interested, skip to the last section for a brief package description, or see the full package here: password-menu.

Making life easier

The need to automate something usually becomes apparent when an often-used workflow is awkward or inefficient. The effort to create the automation will hopefully pay off over time. I.e. ideally, the ROI will be high. This is why Emacs users spend so much time tweaking their configurations, even though I suspect the ROI may not be as high as they'd like!

In my case, the pain point was about the repeated need to provide passwords and tokens in a variety of situations. This is a common scenario — websites, CLI logins, curl/Postman, etc. all need credentials.

In addition to using auth-source (authinfo.gpg) I had gotten into the bad habit of scattering credentials around various org-mode files (unencrypted! 😞). When I needed a password, it required a search, buffer open, select, copy, and then paste to the target to complete. This is (and felt) very clumsy and inefficient. The addition of Org Mode custom link: copy to clipboard made the select/copy a little easier, but the overall experience still sucked.

The search for an existing solution came up empty. This was surprising given that the Emacs package ecosystem (Melpa) is extensive and has been around for a long time.

One of the distinguishing features of Emacs is the ability to customize and extend its functionality with Elisp. So, here we go.

Feature #1: Transient prefix menu

My approach was to use the auth-source-search API along with the transient package to provide the menu UI. This type of "porcelain" has become popular for Emacs projects that have complex user interfaces (Magit! being the best example). My use case is far simpler, but the UI style was still desirable.

The only real Elisp challenge (for me anyway) was creating a dynamic transient prefix list. I couldn't find any examples of this. Transient prefixes are implemented as vectors. Also, note that the transient interface is very complex (Prefixes, Suffixes, Infixes) but this use case only needed prefixes.

A typical transient prefix group looks like this (excerpted from transient-showcase).

Dynamically creating a vector like this can be implemented with this pseudocode:

You won't find this code in password-menu.el because it was refactored with macros as described below.

An Elisp expert could have knocked out this implementation without breaking a sweat, but it was a learning experience for me.

The development of the picker string functionality (1..0, a1..a0, b1..b0,...) was also a lot of fun.

Feature #2: Completing-read menu

All done, right? In the middle of the transient work, I ran across a post that included a good description of using completing-read with a list. The completing-read list interface could leverage the same core transient prefix list elements. Specifically, the user@host string menu item and the lambda that gets the password. The prefix picker string is not needed.

The implementation was refactored with a couple of macros so both UI interfaces could share the list generation. I won't dig into the details here, but the password-menu.el code should be self-explanatory. Getting the completing-read list boiled down to this:

The transient version uses these same two macros, but is a little more complex. This shows the usefulness of macros and was another Elisp learning experience.

Feature #3: Kill ring and clipboard expiration

Finally, while investigating other password management packages I ran across this kill ring expiration implementation. It makes sense to remove the secret from the kill ring and system clipboard automatically so I incorporated their code pretty much unmodified.

This additional functionality is like icing on the cake.

Epilogue

I've been dogfooding password-menu for a while. I use it often and can safely say it has improved the "get credential" experience. Before, it was "Oh crap, here we go...". Now it's simply C-x j 3 from anywhere, then paste. Easy peasy!

The other benefit is that I've now removed all those unencrypted secrets from my org files and have everything tucked away in one secure place: authinfo.gpg. That's a big win too.

The password-menu package

Password-menu is a UI wrapper ("porcelain") for the built-in Emacs auth-source secrets library. This package allows you to display auth-source entries in the minibuffer with either completing-read or transient. The password for the selected entry is copied to the kill ring and system clipboard and automatically removed later.

Transient

Completing-read

Tags: ,

Leave a Reply