diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-09 10:21:36 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-09 10:21:36 -0500 |
| commit | f3dc2a905e4328d6fc16aa15d091ec65edcfc120 (patch) | |
| tree | efa7f2678787db43e387968b1bfab72883c9497d | |
| parent | 1b58db366885bf014d14da19d14da8ee68aff9da (diff) | |
| download | archsetup-f3dc2a905e4328d6fc16aa15d091ec65edcfc120.tar.gz archsetup-f3dc2a905e4328d6fc16aa15d091ec65edcfc120.zip | |
feat: add post-install cmail Bridge setup
Bridge first-run is interactive, so I put the cmail wiring in a post-install
helper rather than running it inside archsetup. scripts/cmail-setup-finish.sh
handles the post-first-run steps idempotently: it decrypts the encrypted
cmailpass, copies Bridge's self-signed cert to ~/.config/protonbridge.pem,
symlinks the cmail-action triage helper into ~/.local/bin, and enables the
user-level protonmail-bridge service.
I added loginctl enable-linger in essential_services so the user service
survives logout — without it, triaging cmail from a remote agent or SSH
session has nothing to talk to. outro prints a four-step runbook for the
manual steps after reboot.
| -rwxr-xr-x | archsetup | 17 | ||||
| -rw-r--r-- | assets/outbox/2026-05-08-protonmail-bridge-cmail-triage.org | 198 | ||||
| -rwxr-xr-x | scripts/cmail-setup-finish.sh | 79 |
3 files changed, 294 insertions, 0 deletions
@@ -1367,6 +1367,15 @@ EOF else display "task" "ext4/other filesystem detected" fi + + # User Services Lingering + # Keeps user-level systemd services (e.g., protonmail-bridge) running without + # an active login session. Needed for headless triage workflows that talk to + # user-level IMAP/SMTP daemons over SSH or from remote agents. + + display "subtitle" "User Services" + action="enabling user-services lingering for $username" && display "task" "$action" + loginctl enable-linger "$username" >> "$logfile" 2>&1 || error_warn "$action" "$?" } ### Xorg Display Manager @@ -2187,6 +2196,14 @@ outro() { fi printf "\n" + printf "If you use Proton Mail Bridge for cmail triage, finish the setup\n" + printf "after reboot:\n" + printf " 1. Clone claude-templates to ~/projects/claude-templates if missing.\n" + printf " 2. Run 'protonmail-bridge --cli', log in, then quit.\n" + printf " 3. Run ~/code/archsetup/scripts/cmail-setup-finish.sh\n" + printf " 4. First mail sync: mbsync cmail && mu index\n" + + printf "\n" printf "Please reboot before working with your new workstation.\n\n" # Completion marker for automated testing diff --git a/assets/outbox/2026-05-08-protonmail-bridge-cmail-triage.org b/assets/outbox/2026-05-08-protonmail-bridge-cmail-triage.org new file mode 100644 index 0000000..8b75c9c --- /dev/null +++ b/assets/outbox/2026-05-08-protonmail-bridge-cmail-triage.org @@ -0,0 +1,198 @@ +#+TITLE: Proton Mail Bridge + cmail-action triage helper +#+DATE: 2026-05-08 + +* What this is + +Notes for folding the Proton Mail Bridge + =cmail-action= triage +helper setup into the archsetup install flow. Reproduces what was +set up on ratio on 2026-05-08. + +* Why + +Proton Mail (=c@cjennings.net=, locally =cmail=) doesn't expose +plain IMAP — it's end-to-end encrypted. Proton Mail Bridge is the +official local daemon that decrypts on-the-fly and presents a +local IMAP/SMTP endpoint at =127.0.0.1:1143= / =1025=. Once Bridge +is running, anything that speaks IMAP (mbsync, mu, mu4e, custom +helpers) talks to cmail the same way it talks to any other IMAP +account. + +The triage workflow at =homelab/.ai/project-workflows/process-unread-emails.org= +previously handled gmail/dmail (via the Gmail MCP server) but had +no equivalent for cmail. The =cmail-action= helper closes that +gap — it talks directly to Bridge's IMAP and provides the same +operations the Gmail MCP gives the other two accounts: list +unread, read body, mark read, star, unstar, trash. Pattern: full +parity, Claude drives end-to-end, no switching to mu4e mid-flow. + +* End state to reproduce + +After the install completes, the system has: + +1. =protonmail-bridge= installed from AUR. +2. First-run done: Proton account logged in, Bridge-generated IMAP + password captured at =~/.config/.cmailpass= (mode 0600). +3. Bridge's self-signed cert at =~/.config/protonbridge.pem=. +4. Bridge running as a systemd user service (autostarts on login). +5. =cmail= account block in =~/.mbsyncrc= pointing at the local + Bridge endpoint. +6. =~/.mail/cmail/= populated by mbsync. +7. =~/.local/bin/cmail-action= symlinked into PATH, pointing at + =~/projects/claude-templates/.ai/scripts/cmail-action.py=. + +* Steps + +Pre-reqs: claude-templates already cloned at +=~/projects/claude-templates= (the cmail-action.py script lives +there), paid Proton plan (Bridge is gated behind paid tier). + +** 1. Install Bridge from AUR + +#+begin_src bash +yay -S protonmail-bridge +#+end_src + +Pulls in =protonmail-bridge= and =protonmail-bridge-core= binaries. +Don't use Flatpak. + +** 2. First-run: log in and capture IMAP password + +This step is interactive and can't be automated — Proton requires +real credentials. Run once on a new install: + +#+begin_src bash +protonmail-bridge --cli +#+end_src + +In the CLI: =login= → enter Proton account credentials → 2FA prompt +if enabled. Bridge stores the account credentials in its own +encrypted state under =~/.config/protonmail/bridge-v3/=. + +Then in the same CLI session, =info= prints the per-app IMAP and +SMTP passwords Bridge generated for this account. Capture the IMAP +password (NOT the Proton account password — these are distinct): + +#+begin_src bash +echo 'BRIDGE_GENERATED_PASSWORD' > ~/.config/.cmailpass +chmod 600 ~/.config/.cmailpass +#+end_src + +The encrypted form lives in +=~/code/archsetup/dotfiles/common/.config/.cmailpass.gpg= for sync +across machines. The plaintext file at =~/.config/.cmailpass= is +gitignored and decrypted locally as needed. + +** 3. Export the Bridge cert + +Bridge ships a self-signed cert that mbsync and =cmail-action= pin +against. Path inside Bridge state varies by version; locate and +copy: + +#+begin_src bash +find ~/.config/protonmail -name 'cert.pem' -print +cp ~/.config/protonmail/bridge-v3/cert.pem ~/.config/protonbridge.pem +#+end_src + +The cert's CN is =127.0.0.1=, so connecting to localhost validates +against the pinned cert. =cmail-action= disables hostname +verification anyway (cert lacks SAN; localhost + pinned cert is +sufficient). + +** 4. Configure mbsync for cmail + +Add the =cmail= block to =~/.mbsyncrc=. The current homelab setup +has it; the canonical version lives in +=dotfiles/common/.mbsyncrc= and is symlinked to =~/.mbsyncrc=. + +Key fields: + +#+begin_example +IMAPAccount cmail +Host 127.0.0.1 +Port 1143 +User c@cjennings.net +PassCmd "cat ~/.config/.cmailpass" +TLSType STARTTLS +CertificateFile ~/.config/protonbridge.pem + +MaildirStore cmail-local +Path ~/.mail/cmail/ +Inbox ~/.mail/cmail/Inbox/ +Trash ~/.mail/cmail/Trash/ +SubFolders Verbatim +#+end_example + +Plus channels for each folder (Inbox / Sent / Trash / Archive / +Starred / Spam — Drafts and All Mail are intentionally omitted +because Bridge has historically had sync issues on those two +folders). + +Initial sync: + +#+begin_src bash +mkdir -p ~/.mail/cmail +mbsync cmail +mu index +#+end_src + +** 5. Enable Bridge as a systemd user service + +The protonmail-bridge AUR package ships a unit at +=/usr/lib/systemd/user/protonmail-bridge.service=. The unit calls +=protonmail-bridge-core --noninteractive= directly (not the +=protonmail-bridge --no-window= wrapper). Enable + start: + +#+begin_src bash +systemctl --user enable --now protonmail-bridge +#+end_src + +Verify: + +#+begin_src bash +systemctl --user status protonmail-bridge +ss -ltn | grep -E '127.0.0.1:(1143|1025)' +#+end_src + +Expected: service active (running), both ports LISTEN. Bridge logs +a non-fatal warning about =pass not initialized= because the +=pass= keychain helper isn't set up — Bridge falls back to its own +encrypted state under =~/.config/protonmail/=. Ignore. + +** 6. Symlink cmail-action into PATH + +The script is the source-of-truth in claude-templates. The PATH +entry is a symlink so updates to claude-templates immediately +take effect: + +#+begin_src bash +ln -s ~/projects/claude-templates/.ai/scripts/cmail-action.py \ + ~/.local/bin/cmail-action +#+end_src + +Verify: + +#+begin_src bash +cmail-action folders # lists Proton IMAP folders +cmail-action list-unread --limit 1 # returns JSON +#+end_src + +* Notes / gotchas + +- *Bridge requires a paid Proton plan.* Bridge auth fails on free + tier. If the install runs on a free-tier account, skip steps 2-6 + with a clear error. +- *Bridge sometimes resets UIDVALIDITY* after upgrades. The + homelab =sync-email.org= workflow documents the recovery steps + (delete =.uidvalidity= and =.mbsyncstate= under + =~/.mail/cmail=, re-run mbsync). Doesn't affect =cmail-action= + because the helper talks to Bridge live and doesn't keep its + own state. +- *The Bridge cert can rotate.* If =cmail-action folders= ever + starts failing with cert errors, re-run step 3. +- *Drafts and All Mail are intentionally omitted from mbsync* per + the comment in =~/.mbsyncrc=. Don't add them back without + testing — historical sync errors. +- *=cmail-action= bypasses mbsync* for triage operations — it + talks to Bridge's IMAP directly. mbsync is still used for + offline reading via mu/mu4e, but the triage workflow doesn't + depend on it. diff --git a/scripts/cmail-setup-finish.sh b/scripts/cmail-setup-finish.sh new file mode 100755 index 0000000..de99101 --- /dev/null +++ b/scripts/cmail-setup-finish.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# cmail-setup-finish.sh — finish Proton Mail Bridge + cmail-action setup after +# Bridge first-run. Idempotent; safe to re-run after a Bridge cert rotation or +# a claude-templates re-clone. +# +# Pre-reqs (the script aborts if any are missing): +# - protonmail-bridge installed (archsetup handles it) +# - You have run 'protonmail-bridge --cli', logged in, and quit at least once +# (the script looks for state at ~/.config/protonmail/bridge-v3/) +# - claude-templates cloned at ~/projects/claude-templates +# - dotfiles stowed (~/.config/.cmailpass.gpg present) +# +# What it does: +# 1. Decrypts ~/.config/.cmailpass.gpg → ~/.config/.cmailpass (mode 0600) +# 2. Copies Bridge's self-signed cert → ~/.config/protonbridge.pem +# 3. Symlinks ~/projects/claude-templates/.ai/scripts/cmail-action.py +# → ~/.local/bin/cmail-action +# 4. Enables + starts the protonmail-bridge user service +# 5. Verifies Bridge is listening on 127.0.0.1:1143 / :1025 + +set -euo pipefail + +err() { printf 'error: %s\n' "$*" >&2; exit 1; } +info() { printf '==> %s\n' "$*"; } +ok() { printf ' %s\n' "$*"; } + +# 1. Pre-reqs +command -v protonmail-bridge >/dev/null 2>&1 \ + || err "protonmail-bridge not found in PATH — install via archsetup first" + +bridge_state="$HOME/.config/protonmail/bridge-v3" +[ -d "$bridge_state" ] \ + || err "Bridge has no state at $bridge_state — run 'protonmail-bridge --cli' and log in first" + +cmail_action_src="$HOME/projects/claude-templates/.ai/scripts/cmail-action.py" +[ -f "$cmail_action_src" ] \ + || err "cmail-action.py not found at $cmail_action_src — clone claude-templates first" + +cmailpass_enc="$HOME/.config/.cmailpass.gpg" +[ -f "$cmailpass_enc" ] \ + || err "$cmailpass_enc not found — ensure dotfiles are stowed" + +# 2. Decrypt cmailpass +info "decrypting $cmailpass_enc" +cmailpass_plain="$HOME/.config/.cmailpass" +gpg --quiet --yes --decrypt --output "$cmailpass_plain" "$cmailpass_enc" +chmod 600 "$cmailpass_plain" +ok "wrote $cmailpass_plain (mode 0600)" + +# 3. Bridge cert +info "exporting Bridge cert" +cert_src="$(find "$bridge_state" -name 'cert.pem' -print -quit 2>/dev/null)" +[ -n "$cert_src" ] || err "no cert.pem found under $bridge_state — Bridge state is incomplete" +cert_dst="$HOME/.config/protonbridge.pem" +cp "$cert_src" "$cert_dst" +ok "copied $cert_src → $cert_dst" + +# 4. Symlink cmail-action +info "symlinking cmail-action" +mkdir -p "$HOME/.local/bin" +ln -sf "$cmail_action_src" "$HOME/.local/bin/cmail-action" +ok "linked $HOME/.local/bin/cmail-action → $cmail_action_src" + +# 5. Enable + start systemd user service +info "enabling protonmail-bridge user service" +systemctl --user enable --now protonmail-bridge +ok "service active" + +# 6. Verify +info "verifying Bridge is listening" +if ss -ltn 2>/dev/null | grep -qE '127\.0\.0\.1:(1143|1025)'; then + ok "127.0.0.1:1143 + :1025 LISTEN" +else + err "Bridge isn't listening on the expected ports — check 'systemctl --user status protonmail-bridge'" +fi + +echo +echo "cmail setup complete." +echo "Next: 'mbsync cmail && mu index' for the first sync." |
