The obvious way to categorize email with a language model is to send the email to a language model. It is also the way that makes your mail somebody else's training data.
We took the longer route.
A small model, close to the mailbox
Categorization runs on a quantized classifier that ships with the client. It sees the sender, the subject, the first few hundred tokens of the body, and a compact feature vector describing your own history with that address.
That last part does most of the work. A receipt from a vendor you pay monthly is not the same message as the first invoice from a stranger, even when the text is nearly identical.
What we gave up
A local model is smaller, so it is worse at rare cases. It will occasionally file a conference invitation under Travel when you consider it Work. We accepted that.
What we did not accept was a design where fixing that mistake required sending the message anywhere. Corrections stay on device and adjust your own weights.
The part that still calls out
Drafting replies is different. Writing a good reply needs a real model, so that request does leave the device — with explicit consent, per thread, and with a visible indicator while it happens.
We think that boundary is the honest place to draw the line: sorting is passive and constant, so it stays local. Writing is deliberate and occasional, so it can ask.

