1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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.
|