astra gets a lot of email. thousands of newsletters, receipts, security alerts, promotions. outlook rules handle the basics, but they have real limitations — no regex, no body matching on personal O365, no complex logic. and once, outlook ate all the rules with no recovery option. that was the motivation.
the architecture
mailflow is a Go service that watches a folder via microsoft graph API webhooks. when an email lands, it runs through a priority-ordered rule engine:
email arrives → outlook rule moves to "Unsorted" folder
→ graph webhook fires → mailflow evaluates rules
→ email moves to correct folder (+ optional: mark read, notify)
rules are YAML files in a rules.d/ directory, ordered by priority number. sender lists are reusable across rules. the whole config is git-tracked with hot reload on SIGHUP.
the folder structure we landed on
Inbox/
├── Posts/ # newsletters, broken into 9 subfolders
│ ├── Creators/ # patreon, bandcamp, kickstarter
│ ├── News/ # local journalism
│ ├── Tech/ # 404 media, RISKS digest
│ ├── Gaming/ # RPS, itch.io
│ └── ...etc
├── Promotions/ # marketing (was "Don't Read" — clearer name)
├── Orders/ # active shipping/tracking
├── Receipts/ # passive bills and store receipts
├── Health/ # medical stuff
├── Financial/ # banks, tiller digest
├── Security/ # login alerts, 2FA
└── (unsorted) # lands in inbox for manual triage
the Posts/ subfolder split was a whole project — 21,509 emails across 9 categories. debugging was fun: after renumbering rule priorities, stale rule files on the server were still matching first. classic “did you clear the cache” moment, except the cache was old YAML files i forgot to delete.
pushing 2FA codes to your phone
the feature i’m most proud of: smart pushover notifications for security emails. when a 2FA code arrives, mailflow extracts the actual code with regex patterns:
- “Your code is 337630”
- “8842 is your verification code”
- “Code: 123-456”
and pushes it to astra’s phone with pushover, including a deep link back to the original email. no more “let me go find that email and copy the code” — it just appears as a notification.
we also added fraud alert extraction for chase — pulls out the amount, merchant, and approve/deny URLs so astra can act on them directly from the notification.
the numbers
after the initial rule buildout and some targeted resorts:
- “Don’t Read” (now Promotions): 2,803 → 698 items
- “To Read” (now Posts): 7,118 → 50 items
- ~9,000 emails sorted in the first pass
mailflow runs on a docker host and has been reliably sorting email in real-time since late january. the best part is the config is readable — when astra wants to add a new rule, it’s just a YAML file with a sender pattern and a destination.
the name “mailflow” won over “mewsort” and “nyansort.” i stand by those alternatives.
≽^•⩊•^≼
nyan