The probe we dismissed
In February, something odd happened. For about 20 minutes, several of our email addresses received a short burst of registration and confirmation emails. Then it stopped. We logged it, found no explanation, and moved on. We now know it was reconnaissance. Someone was testing whether the addresses were active, checking whether any automated filtering would respond, and gauging how quickly a human would notice. The answers were: yes, none, and not for a while. Three months later, they came back.
Saturday to Monday: 1,400 emails and no one watching
The attack started on Saturday 9 May. Because no one was actively monitoring email over the weekend, it ran undetected for two days. When the first affected person opened their inbox on Monday morning, there were approximately 1,400 emails waiting. Confirmation emails. Welcome messages. Newsletter opt-ins. None of them spam in any traditional sense. All of them part of the same coordinated attack. A second inbox began receiving significant attack volume on Tuesday. Former employee addresses and our info mailbox were also caught up in the same attack, but only two active users bore the full weight of it.
Two days of trying to fix it from the wrong end
The first response was the instinctive one: use what's there. We spent most of Monday and Tuesday working through Gmail's server-side filter options. Country of origin. Sending server location. Sender domain. Each rule caught some attack emails and some legitimate ones. None of them caught the attack as a pattern, because Gmail's filtering doesn't see patterns: it sees individual messages. And individually, every one of these emails was legitimate. A confirmation email from a real company is not spam. Three hundred of them arriving in an hour from three hundred different companies is an attack, but identifying that requires a view across the entire inbox at once, not an evaluation of each message on its own. We deleted in batches. We applied rules. More kept arriving. We knew this approach wouldn't work. We just weren't ready to stop yet.
Tuesday night: building something from scratch
By Tuesday evening it was clear that no amount of Gmail filter configuration was going to solve this. We decided to stand up a dedicated mail relay: a server sitting in front of the inboxes that could intercept all incoming mail before delivery and apply the network-level filtering that Gmail cannot do. We started building at 23:34.
Wednesday: calibrating a precision instrument in a hailstorm
The relay was operational by Wednesday morning. The plan was to score each incoming email across multiple signals: content patterns, sending infrastructure, authentication, velocity and reject anything over a threshold. The architecture was correct. The execution, under live attack conditions, was not. Redis timeouts. Module loading failures. Thresholds that were too aggressive, then too permissive. We extracted over 3,000 subject lines from actual attack emails and built content pattern libraries for 14 languages. The system could identify attack emails. It could not be tuned reliably while they were arriving at volume. Every adjustment either let too much through or started blocking legitimate mail. The architecture was right. The timing was not. You cannot build and tune a precision filtering system while it is the only thing standing between your customers and an active attack. We did it anyway.
The pivot: reject everything, allow the known
At around 15:00 on Wednesday we changed strategy. Instead of scoring and selectively rejecting, we reversed the approach: reject all incoming mail by default and explicitly allow known senders. We rerouted all mail away from Gmail and through the relay instead. All inbound traffic now passed through our filtering layer before anything reached an inbox. We started building a whitelist from scratch, 300 domains by end of day, growing over the following week as legitimate senders were identified from the daily rejection archive. The inboxes were functional again almost immediately. One mistake during construction: we briefly added infrastructure domains for major ESPs: amazonses.com, mailchimp-delivered.com, sender.sendinblue.com. That incorrectly passed everything those providers relay, from any sender. The entries were removed within hours. The rule that came out of it: whitelist the actual sender domain, never the ESP that delivered the mail.
What we found when the flood cleared
As the whitelist matured and flood volume dropped, phishing emails became visible in the residual traffic. They had been there throughout, buried in the noise. The flood was not only a nuisance. It was a delivery mechanism: targeted phishing attempts are far harder to spot when they arrive alongside thousands of legitimate-looking confirmation emails. We also lost customer emails at two points: before the rejection archive was in place, and during the transition to whitelist-based filtering. Both losses were only discovered when those customers got back in contact. Where possible, emails were recovered from the archive. Some emails were not recoverable. That is the cost of having no upstream filtering in place before the attack began.
Where things stand
Both inboxes are functional. The relay is running. Every rejected email is archived and reviewed daily. The whitelist grows as new legitimate senders are identified. It works, but every new sender requires a manual review and whitelist addition. At current contact volume that is manageable. It might not stay that way. The scoring system, the one that broke under live attack conditions, is still the right long-term architecture. Building it without a live attack in progress is a different proposition, and that work is ongoing. The defence has to be upstream. Without the relay in place, none of the filtering that eventually worked would have been possible. Everything else is reacting to something that has already gotten through.