emacs-patch-review
Port of Thunderbird Patch Review to mu4e.
git clone git://mccd.space/emacs-patch-reviewcommit 9fc1d7718e55fd9b42df6480abcff7f43fa4c6cd
parent 4a448f791e7489d23b0646d756eebae3354e360c
Author: Pi Agent <agent@pi.local>
Date: Mon, 3 Aug 2026 11:19:08 +0200
Add mail-auth: cache passage-backed IMAP passwords on tmpfs
passage decrypts against an age identity on a passkey (YubiKey) which
needs a physical touch per call; running it on every mbsync cycle is
impractical. mail-auth copies each one-line password into
$XDG_RUNTIME_DIR/mbsync/<acct>.pass (tmpfs, 0700), leaves it there until
reboot/logout, and only re-asks passage when it is missing. A
MBSYNC_PW_TTL setting optionally rotates it on a timer.
Diffstat:
| A | mail/mail-auth | | | 64 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 64 insertions(+), 0 deletions(-)
diff --git a/mail/mail-auth b/mail/mail-auth
@@ -0,0 +1,63 @@
+#!/bin/sh
+# mail-auth -- cache passage-backed IMAP passwords for mbsync.
+#
+# `passage show` decrypts against an age identity kept on a passkey (often a
+# YubiKey), which requires a physical *touch* for every decryption. Doing
+# that on every mbsync run is impractical, so this helper copies each
+# one-line password onto tmpfs ($XDG_RUNTIME_DIR, owned by you and wiped on
+# reboot/logout) and leaves it there. mbsync's PassCmd then just reads the
+# cached file -- no passkey touch.
+#
+# The cache lives until reboot; touch the passkey again only when it's gone.
+# Set MBSYNC_PW_TTL=N (seconds) to *also* rotate it after N seconds of
+# uptime, which is useful on long-running hosts.
+#
+# Usage: mail-auth
+# Exit status: 0 if every needed cache is usable (fresh); 1 if one could not
+# be obtained (so the caller may skip that cycle and retry).
+
+set -eu
+
+RUNTIME="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
+CACHEDIR="$RUNTIME/mbsync"
+mkdir -p "$CACHEDIR"
+chmod 700 "$CACHEDIR"
+
+PASSAGE_TIMEOUT="${MBSYNC_PASSAGE_TIMEOUT:-30}" # seconds you have to tap the key
+TTL="${MBSYNC_PW_TTL:-0}" # 0 => keep cache indefinitely
+
+log() { printf 'mail-auth: %s\n' "$*" >&2; }
+
+fetch() {
+ entry="$1"; out="$2"
+
+ # Still fresh? Done.
+ if [ -f "$out" ]; then
+ if [ "$TTL" -gt 0 ]; then
+ now=$(date +%s); mtime=$(stat -c %Y "$out" 2>/dev/null || echo 0)
+ if [ $((now - mtime)) -lt "$TTL" ]; then
+ log "cache fresh ($entry)"
+ return 0
+ fi
+ else
+ log "cache present ($entry, TTL disabled)"
+ return 0
+ fi
+ fi
+
+ log "fetching $entry -- touch your passkey within ${PASSAGE_TIMEOUT}s"
+ if pw="$(timeout "$PASSAGE_TIMEOUT" passage show "$entry" 2>/dev/null | head -n1)" \
+ && [ -n "$pw" ]; then
+ umask 077
+ printf '%s\n' "$pw" > "$out"
+ log "cached $entry -> $out"
+ return 0
+ fi
+ log "FAILED to fetch $entry (passkey not touched? aborting login $entry)"
+ return 1
+}
+
+status=0
+fetch "piva/gmail" "$CACHEDIR/gmail.pass" || status=1
+fetch "fastmail/marcc" "$CACHEDIR/fastmail.pass" || status=1
+exit $status
+\ No newline at end of file