Skip to content

kumo.configure_log_disposition_hook

kumo.configure_log_disposition_hook { PARAMS }
Since: Version 2025.10.06-5ec871ab

The functionality described in this section requires version 2025.10.06-5ec871ab of KumoMTA, or a more recent version.

Registers a synchronous logging hook. When a matching log event is generated, the log_disposition_NAME event is triggered, where NAME is the value of the name field passed to this function. The registered event handler is called with the originating Message and the JsonLogRecord that describes the event.

kumo.on('init', function()
  kumo.configure_log_disposition_hook {
    name = 'ndr_generator',
  }
end)

kumo.on('log_disposition_ndr_generator', function(msg, log_record)
  local bounce_msg = kumo.generate_rfc3464_message({
    include_original_message = 'FullContent',
    enable_bounce = true,
    reporting_mta = {
      mta_type = 'dns',
      name = 'mta1.example.com',
    },
  }, msg, log_record)
  if bounce_msg then
    kumo.inject_message(bounce_msg)
  end
end)

Unlike kumo.configure_log_hook, which queues a copy of the log record for asynchronous delivery through the normal queueing machinery, the disposition hook runs synchronously as part of processing the disposition of the message. The event handler is invoked before control returns to the caller that produced the log event.

This synchronous behavior guarantees that the message is still present in the spool when the hook runs, which matters because a terminal disposition such as a permanent failure removes the message from the spool once the disposition completes. A hook that needs to read the originating message, such as to generate a bounce report from its content, must use this synchronous form: an asynchronous hook does not run until after the disposition has completed, by which point that removal may have already taken the message out of the spool.

The primary use case is generating RFC 3464 delivery status notifications in response to delivery failures. See kumo.generate_rfc3464_message.

Messages generated by kumo.configure_log_hook are never passed to the disposition hook.

name

Required string naming the hook.

The log_disposition_NAME event, where NAME is this value, is triggered when a matching log event is generated. The name must be unique across all configured logging hooks.

per_record

Optional map that controls which record types are passed to the hook. This has the same shape as record type, although only the enable boolean is used by the disposition hook. Set enable to false to exclude that record type from the hook.

When per_record is omitted, every record type is enabled.

The following restricts the hook to Bounce records, which is typical for a hook that only generates bounce reports:

kumo.on('init', function()
  kumo.configure_log_disposition_hook {
    name = 'ndr_generator',
    per_record = {
      Any = {
        enable = false,
      },
      Bounce = {
        enable = true,
      },
    },
  }
end)

Using the log_hooks helper

The policy-extras.log_hooks module provides a new_disposition_hook helper that makes it a little more convenient to set up a disposition hook.

local log_hooks = require 'policy-extras.log_hooks'

log_hooks:new_disposition_hook {
  name = 'ndr_generator',

  -- Optionally filter/restrict which log records will trigger
  -- and call your `hook` function below.
  -- (*Since: Dev Builds Only*)
  log_parameters = {
    per_record = {
      Any = { enable = false },
      Bounce = { enable = true },
    },
  },

  -- The hook callback, receives the message and log record as
  -- described in the configure_log_disposition_hook documentation above.
  hook = function(msg, log_record)
    local bounce_msg = kumo.generate_rfc3464_message({
      include_original_message = 'FullContent',
      enable_bounce = true,
      reporting_mta = {
        mta_type = 'dns',
        name = 'mta1.example.com',
      },
    }, msg, log_record)
    if bounce_msg then
      kumo.inject_message(bounce_msg)
    end
  end,
}