Build log · Phase 125

Virus/malware scanning

August 22, 2026 · 2 min read · kritchie

The eighth item off the “genuinely zero” list, a near mirror of Spam Filtering with a different scanner, and a chance to fix a real bug the spam feature had been carrying since it shipped.

Same filter-chain pattern as spam filtering, deliberately

The antivirus scanner plugs into the mail server as another optional filter alongside DKIM signing and, when enabled, spam filtering. The scanner and its signature updater run continuously whether or not the toggle is on; only the mail filter itself is controlled by the admin toggle. Two on-infected actions are offered: reject at delivery with a clean bounce (the recommended default) or silently discard. There is no quarantine mailbox in v1, since reject or discard covers the real need without inventing new infrastructure.

Bug #1, caught before it shipped: filter interaction

Spam filtering’s configuration action rebuilt the whole filter list from its own enabled flag with no knowledge of other filters. Writing the virus action the same way would have meant toggling either feature silently removed the other. Each action now checks the other feature’s live state before composing the chain. Verified live in both directions.

Bug #2, caught live: duplicated header comment

The scanner’s config block removed its own directives before rewriting but not its managed-by header comment, so repeated toggles stacked duplicate header lines. Harmless but untidy; fixed.

Bug #3: an environment quirk, tolerated

Enabling these older-style service units occasionally failed with a spurious error from the compatibility layer, not reproducible on retry and already present in shipped spam-filtering code. It is now non-fatal for both, since the restart before it is what matters for routing.

Verified

Live against the dev server and a linked Mail Only satellite: a message carrying the standard EICAR test string was rejected at delivery with the filter named in the mail log, a clean message in the same run was delivered, and enabling spam filtering at the same time left both filters in place in both directions, with identical results on the satellite.

7 new tests; full suite 1,784 and the code-style check green. Two follow-up commits fixed the filter interaction gap and the header duplication, both caught in live verification rather than by the suite.

Phase 125 of 269The build log so far

Every phase, from the beginning →

Leave a comment

Your email address is never published. Comments are checked before they appear.