Mail workflows

Integrations

Connect sc to Dovecot, Rspamd, or another mail workflow that can submit full message content.

sc is designed to fit into existing mail operations. It can return a report for review, generate abuse-report evidence, or ask a configured server to send abuse reports when the request explicitly includes abuse_report.send.

The examples below are starting points. Adjust timing, user selection, logging, redaction, rate limits, and failure handling for your environment before running them unattended.

Dovecot Junk purge job

A Dovecot job can periodically inspect a user’s Junk mailbox, report messages that have been there long enough for user review, and mark each message so it does not get reported again.

This example reports messages in Junk that are between 24 and 48 hours old and do not already have the $AbuseReported keyword. When sc accepts the report, the script adds that keyword to the message.

#!/usr/bin/env sh

set -euo pipefail

USER="you@example.com"
FLAG='$AbuseReported'
SC_URL="https://sc.example.com"

doveadm search -u "$USER" mailbox Junk before 24h since 48h not keyword "$FLAG" |
  while read -r guid uid; do
    tmp="$(mktemp)"

    doveadm fetch -u "$USER" text mailbox-guid "$guid" uid "$uid" | sed 1d > "$tmp"

    if jq -n --rawfile message "$tmp" \
        '{message: $message, abuse_report: {send: true}}' |
       curl -fsS \
         -H 'Content-Type: application/json' \
         --data-binary @- \
         "$SC_URL"
    then
      doveadm flags add -u "$USER" "$FLAG" mailbox-guid "$guid" uid "$uid"
    else
      echo "Failed guid=$guid uid=$uid" >&2
    fi

    rm -f "$tmp"
  done

Use this pattern when users or filters already move unwanted mail into Junk and you want a delayed, repeat-safe reporting workflow.

Rspamd Lua postfilter

Rspamd can report messages it rejects by posting the rejected message content to sc from a postfilter callback.

Put a script like this in /etc/rspamd/rspamd.local.lua, set SC_URL in the Rspamd environment, and make sure your sc server has abuse_report SMTP settings configured if you request sending.

local rspamd_logger = require "rspamd_logger"
local rspamd_http = require "rspamd_http"
local ucl = require "ucl"

rspamd_logger.infox(rspamd_config, "loading sc_report.lua")

local SC_URL = os.getenv("SC_URL")
local TIMEOUT = 15.0

local function sc_report(task)
  if task:get_metric_action() ~= "reject" then
    return
  end

  local content = task:get_content()
  if not content then
    rspamd_logger.errx(task, "no message content available")
    return
  end

  local body = ucl.to_json({
    message = tostring(content),
    abuse_report = {
      send = true,
    },
  })

  local ok = rspamd_http.request({
    task = task,
    url = SC_URL,
    method = "POST",
    body = body,
    headers = {
      ["Content-Type"] = "application/json",
    },
    timeout = TIMEOUT,
    callback = function(err, code, response)
      if err or not code or code < 200 or code >= 300 then
        rspamd_logger.errx(task, "SC report failed: err=%s code=%s response=%s",
          err or "none", tostring(code), response or "")
        return
      end

      local parser = ucl.parser()
      local parsed_ok, parse_err = parser:parse_string(response or "")
      if not parsed_ok then
        rspamd_logger.errx(task, "SC report returned invalid JSON: %s response=%s",
          parse_err or "unknown parse error", response or "")
        return
      end

      local result = parser:get_object()
      local report = result and result.abuse_report

      if report and report.sent == false then
        rspamd_logger.errx(task, "SC report completed with send failures: %s",
          response or "")
      else
        rspamd_logger.infox(task, "SC report succeeded")
      end
    end,
  })

  if not ok then
    rspamd_logger.errx(task, "SC report could not be scheduled")
  end
end

rspamd_config:register_symbol{
  name = "SC_REPORT",
  type = "postfilter,callback",
  flags = "nostat,ignore_passthrough",
  callback = sc_report,
}

Use this pattern when you want rejected mail to generate reports immediately, without waiting for a user-visible Junk folder workflow.

Integration checklist

  • Send the full RFC-822 message in the JSON message field.
  • Use abuse_report.send: true only when the sc server is configured to send mail and you want synchronous delivery.
  • Keep retry behavior conservative so a temporary sc outage does not create duplicate reports.
  • Preserve a local marker, such as a Dovecot keyword, when reporting stored messages.
  • Review privacy tradeoffs before forwarding evidence that may identify real recipients.

Source wiki pages: Integrations, Dovecot_Purge, and Rspamd-Lua.