emacs-patch-review

Port of Thunderbird Patch Review to mu4e.

git clone git://mccd.space/emacs-patch-review
commit 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:
Amail/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