Skip to content

Unreleased Changes in The Mainline

Breaking Changes

  • We now normalize line breaks in SMTP client responses into spaces, to make it a little easier to deal with multi-line responses in both TSA automation, response rewriting and bounce classification. Thanks to @cai-n! #157
  • We now also strip the enhanced status code prefix from each subsequent line of multi-line responses, rather than just the first line. This may influence your classification or TSA automation regexes. For example, for a response like 500-5.5.5 foo\n500 5.5.5 bar we would previously represent that with a logged response.content value of foo\n5.5.5 bar, but will now encode it as foo bar. See #157 for more context.

Other Changes and Enhancements

  • Added a default timeout of 60 seconds to the HTTP client returned from kumo.http.build_client. Added request:timeout() method to explicitly set a timeout value.
  • You may now list multiple regexs and/or multiple actions for TSA rules by using an array in your toml file. Single values are still supported. #99
  • Added over_sign optional to easily enabled DKIM over-signing to protect your messages against replay attacks. The same option can be set for the ed25519_signer as well. #111
  • Updated RocksDB from 8.1 to 8.10
  • Slightly relaxed the MIME parser so that we can tolerate non-conforming 8-bit message bodies with missing transfer encoding when all we need is to parse headers. The invalid messages will trigger NEEDS_TRANSFER_ENCODING when using msg:check_fix_conformance() to validate messages, but won't cause header parsing to fail in general. These non-compliant messages will be parsed (or fixed) using a lossy decode to UTF-8 that will remap invalid bytes to U+FFFD the Unicode Replacement Character.
  • Added max_message_rate option to kumo:make_queue_config to configure the rate at which a given Scheduled Queue can feed into the Ready Queue.
  • Added request_body_limit option to raise the default HTTP request size, which is useful when performing HTTP based injection with large message payloads.
  • It is now possible to use protocol in the queues.toml lua helper configuration file. Thanks to @aryeh! #155
  • The TSA Suspend action will now generate suspensions that are visible via the HTTP API and kcli utility, and that will take effect in realtime.
  • TSA now supports SuspendTenant and SuspendCampaign actions that allow reacting to source-domain-specific tempfails. These will also be visible via the HTTP API and kcli utility, and also take effect in realtime.
  • New glob, read_dir and uncached_glob filesystem functions. #161
  • New kumo.api.inject.inject_v1 lua function for constructing and injecting arbitrary messages via policy. #102
  • 4xx and 5xx rejections made by the SMTP server are now logged as Rejection log records. #88
  • New msg:set_data, msg:append_text_plain, msg:append_text_html message methods for amending the message content, which can be used to inject tracking pixels. #120
  • kcli bounce (and the underlying bounce HTTP API) no longer wait for the contents of matching scheduled queues to be removed from the spool. This reduces contention in the system for large queues, but means that the stats returned to the initial request will generally be lower than in previous releases. You can use kcli bounce-list or the corresponding bounce HTTP API to retrieve the totals asynchronously.
  • kumo.memoize now includes thundering herd protection, which constrains the concurrency of the cache population function when there is a cache miss.
  • now also supports a connect_timeout option. #168
  • Added kcli top command for watching system status from the comfort of your terminal.
  • The Prometheus Metrics Endpoint now supports an optional prefix parameter to rewrite metrics names with a prefix. Thanks to @cai-n! #179


  • The delivered_this_connection counter was incorrectly double-counted for SMTP sessions, effectively halving the effective value of max_deliveries_per_connection.
  • msg:set_scheduling wouldn't take effect until after the first transient failure.
  • Re-run the ready queue maintainer immediately after closing a connection due to reaching the max_deliveries_per_connection, so that new connection(s) can be established to replace the one that just closed. Previously, we would only do this once every minute. #116
  • The smtp_client_rewrite_delivery_status event could trigger with incorrect scheduled queue name components.
  • webhooks and other lua delivery handlers didn't reuse connections correctly. Thanks to @cai-n! #135
  • OOB and ARF reports were incorrectly logged as Reception records
  • OOB reports did not respect headers and meta configured in the logger
  • MIME Parser would discard whitespace from improperly encoded message/rfc822 parts when rebuilding messages.
  • proxy-server didn't actually bind to the requested source address
  • listener_domains.lua helper didn't always fallback to full wildcard/default (*) entries correctly. #128
  • smtp client did not always wait for the full extent of the idle timeout for new messages before closing the connection.
  • Potential deadlock on systems with very low core counts when MTA-STS is enabled, and sending to domain(s) with an MTA-STS policy.
  • If a TSA rule suspends a ready queue in response to a 421-before-MAIL-FROM, the contents of the corresponding ready queue could get stuck
  • When log_arf or log_oob are set to true with relay_to=false, we now return a 550 error response for messages that are not ARF or OOB reports. Previously, we would return a 250 response and silently drop the message in this case, which gave the false impression that it was accepted for relaying.
  • Large connection_limit values combined with large ready queues could result in more connections than necessary being opened.
  • A client issuing RCPT TO outside of a transaction could cause the service to panic.