Download Latest Version v1.5.0 source code.tar.gz (689.0 kB)
Email in envelope

Get an email when there's a new version of Addy

Home / v1.4.0
Name Modified Size InfoDownloads / Week
Parent folder
README.md 2026-04-08 4.8 kB
v1.4.0 source code.tar.gz 2026-04-08 677.0 kB
v1.4.0 source code.zip 2026-04-08 1.0 MB
Totals: 3 Items   1.7 MB 0
  • Added new blocklist feature that allows you to block senders by their email address or entire domain
  • Added List-Unsubscribe behaviour setting
  • Added quick option to add sender to blocklist from failed deliveries page

Breaking Changes

  1. The new blocklist feature requires an update to Rspamd config in order to work.

You must create a new file named addy_blocklist.lua at /etc/rspamd/lua.local.d/addy_blocklist.lua, in this file enter the following contents (making sure to change blocklist_api_url and blocklist_secret to your own values):

:::lua
--[[
  Rspamd Lua script: user blocklist check via Laravel HTTP API.

  Deploy this on each mail server that runs Rspamd. Point blocklist_api_url
  at your Laravel app.

  Required: rspamd_http (built-in). Symbol BLOCKLIST_USER is set when the
  API returns block=true; map this symbol to an action (reject/discard) in
  your Rspamd actions config.
--]]

local blocklist_api_url = 'https://your-addy-instance.com/api/blocklist-check'
local blocklist_secret = ''  -- same as BLOCKLIST_API_SECRET in .env, or leave '' if unset

-- Simple percent-encode for query parameter values (rspamd_http has no escape)
local function url_encode(s)
  if s == nil or s == '' then return '' end
  s = tostring(s)
  return (s:gsub('[^%w%-_.~ ]', function(c)
    return string.format('%%%02X', string.byte(c))
  end):gsub(' ', '%%20'))
end

local logger = require "rspamd_logger"
local rspamd_http = require 'rspamd_http'

rspamd_config:register_symbol({
  name = 'BLOCKLIST_USER',
  callback = function(task)
    local rcpts = task:get_recipients('smtp')
    local from_env = task:get_from('smtp')
    if not rcpts or #rcpts == 0 then
      logger.infox('blocklist: skip - missing recipient')
      return false
    end
    local recipient = (rcpts[1].addr and rcpts[1].addr:lower()) or ''

    local sender = ''
    if from_env and from_env.addr then
      sender = from_env.addr:lower()
    end

    local from_email = ''
    local from_hdr = task:get_header('From')
    if from_hdr then
      local raw = (type(from_hdr) == 'table') and (from_hdr[1] or from_hdr) or from_hdr
      raw = tostring(raw)
      from_email = raw:match('<([^>]+)>') or raw:match('%S+@%S+') or ''
      from_email = from_email:lower()
    end
    if from_email == '' then
      from_email = sender
    end

    if recipient == '' or (sender == '' and from_email == '') then
      logger.infox('blocklist: skip - missing recipient or from (recipient=%1, sender=%2, from_email=%3)', recipient, sender, from_email)
      return false
    end

    local url = blocklist_api_url
      .. '?recipient=' .. url_encode(recipient)
      .. '&from_email=' .. url_encode(from_email)

    local req_headers = {}
    if blocklist_secret ~= '' then
      req_headers['X-Blocklist-Secret'] = blocklist_secret
    end

    rspamd_http.request({
      url = url,
      headers = req_headers,
      timeout = 2.0,
      task = task,
      callback = function(err_message, code, body, _headers)
        if err_message then
          logger.warnx('blocklist: HTTP error - %1', err_message)
          return
        end
        if code == 200 and body and body:match('"block"%s*:%s*true') then
          task:set_pre_result('reject', '550 5.1.1 Address not found')
          task:insert_result(true, 'BLOCKLIST_USER', 1000.0, '550 5.1.1 Address not found')
          logger.infox('blocklist: BLOCKLIST_USER set for recipient=%1 from_email=%2', recipient, from_email)
        end
      end,
    })

    return false  -- do not match symbol here; only HTTP callback may add it via insert_result
  end,
  score = 1000.0,
})

You also need to update your .env file with the following new values:

# Blocklist API (Rspamd): comma-separated IPs allowed to call /api/blocklist-check; optional shared secret
BLOCKLIST_API_ALLOWED_IPS=127.0.0.1
BLOCKLIST_API_SECRET=

Make sure to add your own server's IP to the list above. If you have chosen to set a shared secret then make sure BLOCKLIST_API_SECRET is the same value as whatever you've chosen above in addy_blocklist.lua.

  1. Upgrade to Vite [8](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#800 may require you to update your node version to 20.19+.
Source: README.md, updated 2026-04-08