pymilter  1.0.5
Writing Milters in Python

At the lowest level, the milter module provides a thin wrapper around the sendmail libmilter API. This API lets you register callbacks for a number of events in the process of sendmail receiving a message via SMTP. These events include the initial connection from a MTA, the envelope sender and recipients, the top level mail headers, and the message body. There are options to mangle all of these components of the message as it passes through the milter.

At the next level, the Milter module (note the case difference) provides a Python friendly object oriented wrapper for the low level API. To use the Milter module, an application registers a 'factory' to create an object for each connection from a MTA to sendmail. These connection objects must provide methods corresponding to the libmilter event callbacks.

Each callback method returns a code to tell sendmail whether to proceed with processing the message. This is a big advantage of milters over other mail filtering systems. Unwanted mail can be stopped in its tracks at the earliest possible point. The callback return codes are milter.CONTINUE, milter.REJECT, milter.DISCARD, milter.ACCEPT, milter.TEMPFAIL, milter.SKIP, milter.NOREPLY.

The Milter.Base class provides default implementations for event methods that do nothing, and also provides wrappers for the libmilter methods to mutate the message. It automatically negotiates with MTA which protocol steps need to be processed by the milter, based on which callback methods are overridden.

The Milter.Milter class provides an alternate default implementation that logs the main milter callbacks, but otherwise does nothing. It is provided for compatibility.

The mime module provides a wrapper for the Python email package that fixes some bugs, and simplifies modifying selected parts of a MIME message.


The libmilter library which pymilter wraps handles all signals itself, and expects to be called from a single main thread. It handles SIGTERM, SIGHUP, and SIGINT, mapping the first two to smfi_stop and the last to an internal ABORT.

If you use python threads or threading modules, then signal handling gets confused. Threads may still be useful, but you may need to provide an alternate means of causing graceful shutdown.

You may find the multiprocessing module useful. It can be a drop-in replacement for threading as illustrated in

python packages for milters

pymilter - this package.

pyspf checks the SMTP envelope sender (MAIL FROM, passed to the Milter.Base.envfrom callback) against a Sender Policy published in DNS by the sending domain. This can prevent forgery of the MAIL FROM. SPF is Sender Policy Framework.

pydkim checks a DKIM signature of the email body and headers against a public key published in DNS by the signing domain. DKIM is DomainKeys Identified Mail.

The authres module parses and formats the Authentication-Results email header, providing a standard place to summarize the results from DKIM, SPF, rDNS, SMTP AUTH, and other email authentication methods.

pydspam wraps the libdspam API of the DSPAM project.

pysrs rewrites MAIL FROM to include a timestamped signature so that "bounce spam" can be immediately rejected.

pygossip is a system to track reputation by domain and authentication level and type, and a simple protocol to gossip about reputations with other mail servers.

written with pymilter

Verify Domain is a Postfix milter that rejects/fixes manipulated From: header on a mail host with multiple virtual domains.

BMS Milter has several milters, a big complicated spam filter that integrates multiple authentication protocols with pydspam, and two simple ones: and