OmniFBL Feedback Loop
OmniFBL is PostMTA's multi-ISP feedback loop aggregation system. It ingests ARF-format complaint reports from five ISP sources, normalises them into a unified internal model, and drives both the suppression list and Kumo Shaping reputation engine in real time.
FBL Overview
A Feedback Loop (FBL) is a service offered by large ISPs (Gmail, Microsoft, Yahoo, Apple, etc.) that forwards complaint reports — messages their users mark as spam — back to the sending MTA via a standardised Abuse Feedback Reporting Format (ARF, RFC 5965) message.
PostMTA operates feedback-monitor@postmaster.<sending-domain> as the ingestion address. When a recipient clicks "Report Spam" in their mail client, the ISP forwards the original message (or its headers) to this address. OmniFBL processes that report and:
- Extracts the complained-about recipient address.
- Suppresses that address in the subuser's suppression list (30-day TTL).
- Applies a reputation penalty to the sending subuser's Kumo Shaping epoch.
- Optionally forwards the raw ARF to a SIEM or security data pipeline.
ARF / RFC 5965 Message Structure
ARF messages are MIME multipart messages with two body parts. Part 1 contains the original message headers (the reported message). Part 2 contains a JSON or plain-text feedback report with machine-readable metadata.
Content-Type: message/feedback-report
From: abuse@hotmail.com
To: postmaster@example.com
Subject: Feedback Report
MIME-Version: 1.0
--boundary
Content-Type: text/rfc822-headers
Content-Disposition: inline
From: newsletter@example.com
To: recipient@hotmail.com
Subject: July Newsletter
DKIM-Signature: v=1; a=rsa-sha256; d=example.com...
Received: from mail.example.com (192.0.2.1) ...
--boundary
Content-Type: application/json
Content-Disposition: inline
{
"feedbackType": "abuse",
"userAgent": "Microsoft Outlook/16.0",
"arrivalDate": "2026-07-18T09:15:00Z",
"messageId": "",
"reportedDomain": "example.com",
"gateway": "Hotmail FBL"
}
--boundary-- OmniFBL supports the following feedbackType values from RFC 5965:
abuse— User marked the message as spam. Processed as a suppression + reputation event.fraud— Phishing or spoofing report. Immediate hard block on the subuser + operator alert (PMH-SEC-034).dsn— Delivery Status Notification (bounce). Routed to bounce processor, not the complaint pipeline.other— Vendor-specific; inspected and discarded if unrecognised.
ISP Sources
OmniFBL maintains a separate ingress channel for each ISP. Configuration for each source is in Settings → Feedback Loops:
| ISP | FBL Address Pattern | ARF Variant | Requires | Latency |
|---|---|---|---|---|
| Gmail | PostMTA-managed (abuse reports go to Gmail's FBL registration) | Gmail ARF extension | DMARC p=quarantine/reject + DKIM pass | < 5 min |
| Microsoft (Hotmail / Outlook / Live) | feedback-monitor@postmaster.<domain> | Standard RFC 5965 + JMR headers | SPF aligned + DKIM pass | < 10 min |
| Yahoo | Registered via Yahoo Postmaster Tools | Yahoo ARF variant | DMARC p=reject + domain verification | < 15 min |
| Apple (iCloud / Me.com) | SAML assertion to Apple FBL endpoint | Apple Feedback ID format | Apple FBL opt-in approval | < 30 min |
| Custom ISP | Operator-configured email address | RFC 5965 or JSON | Manual ISP registration | Variable |
p=quarantine or p=reject. Domains with p=none are not eligible for Gmail FBL.Complaint Pipeline Flow
Every ARF report passes through the following deterministic pipeline:
1. FBL Ingress
MUA reports message as spam via ISP abuse button
ISP forwards ARF report to PostMTA's feedback-monitor address
Mail transport agent (MX) receives the ARF MIME message
SMTP envelope: MAIL FROM:<> (null return path)
2. ARF Parse
Extract Feedback-Type (abuse / fraud / dsn)
Extract original message headers (From / To / Message-ID / DKIM)
Validate that reported domain matches a verified sending domain
Reject malformed ARF with PMH-FBL-004
3. Suppress
Normalize complaint recipient address (lowercase, strip + addressing)
Write record to complaints cache: recipient / source_isp / reported_at
Add to subuser suppression list with 30-day TTL
4. Shape
Read current reputation delta from complaints cache
Apply reputation penalty: -0.02 per unique complaint in rolling 7-day window
If subuser complaint rate exceeds 0.1%:
Flag for operator review
If complaint rate > 0.3%:
Demote subuser epoch by 2 levels
Log event type complaint_demoteAddress Normalisation
Before writing to the suppression list, OmniFBL normalises the recipient address using the same rules as the sending pipeline:
- Convert to lowercase.
- Strip
+addressing (e.g.,user+tag@example.com→user@example.com). - Remove dots from Gmail local-part (handled by Gmail FBL only).
- Query suppression_lists table; skip if already suppressed.
OmniFBL vs Traditional FBL
+------------------+---------------------------+---------------------------+
| Feature | Traditional FBL | OmniFBL |
+------------------+---------------------------+---------------------------+
| ISP sources | 1 (single feedback loop) | 5 (Gmail/Microsoft/ |
| | | Yahoo/Apple/custom) |
| Format handling | One fixed parser | Per-ISP ARF variant parser|
| Suppression sync | Manual via dashboard | Automatic within 60s |
| Shaping coupling | None | Reputation penalty applied |
| | | to epoch on each complaint|
| Complaint rate | Aggregate only | Per-ISP and aggregate |
| alerting | | with configurable阈值 |
| SIEM export | Syslog only | Webhook + Kafka + S3 |
| Header injection | None | X-PMH-FBL-* trace headers |
| Encryption | STARTTLS optional | MTA-STS enforced |
| Deduplication | None | SHA256(recipient+source) |
+------------------+---------------------------+---------------------------+Complaint Rate SQL Query
Use this query to monitor complaint rate per subuser over a rolling 7-day window. Alert threshold is configurable but defaults to 0.1%.
-- Complaint rate per subuser, rolling 7-day window
SELECT
subuser_id,
date_trunc('day', reported_at') AS day,
COUNT(DISTINCT recipient_address) AS unique_complaints,
COUNT(DISTINCT recipient_address) * 100.0
/ NULLIF((
SELECT COUNT(DISTINCT recipient_address)
FROM message_events
WHERE event_type = 'delivered'
AND subuser_id = c.subuser_id
AND event_time >= NOW() - INTERVAL '7 days'
), 0) AS complaint_rate_pct,
(
SELECT COUNT(*)
FROM shaping_epochs
WHERE subuser_id = c.subuser_id
AND date(next_epoch_at) = CURRENT_DATE
) AS current_epoch
FROM complaints c
WHERE reported_at >= NOW() - INTERVAL '7 days'
GROUP BY subuser_id, day
ORDER BY complaint_rate_pct DESC;complaint_threshold_exceeded event. At 0.3%, the subuser's Kumo Shaping epoch is demoted by 2 levels and a PagerDuty alert fires. Configure thresholds in Settings → Alerting → Complaint Rate.SIEM Export
Raw ARF events can be forwarded to a SIEM (Splunk, Elastic, Sumo Logic, etc.) via webhook, Kafka, or S3. The export API supports NDJSON and CSV formats and allows filtering by ISP source and feedback type.
POST /v1/fbl/siem/export
Content-Type: application/json
Authorization: Bearer pmta_live_...
{
"start_time": "2026-07-01T00:00:00Z",
"end_time": "2026-07-18T23:59:59Z",
"format": "ndjson",
"destination": {
"type": "webhook",
"url": "https://siem.example.com/ingest/postmta-fbl"
},
"filters": {
"source_isp": ["gmail", "microsoft"],
"feedback_type": ["abuse"]
}
}
-- Response:
{
"export_id": "fbl_exp_01HX...",
"status": "queued",
"record_count": null,
"eta_seconds": 120,
"webhook_secret": "fbl_whsec_..."
}Supported SIEM destination types:
webhook— POST NDJSON batch to an HTTPS endpoint. Requiresfbl_whsec_*secret for HMAC verification.kafka— Produce to a Kafka topic. Requires bootstrap servers and a SASL credential.s3— Write NDJSON to an S3 prefix. Requires an IAM role ARN.
X-PMH-FBL-* Trace Headers
OmniFBL injects the following trace headers into messages it processes, visible in the raw email headers of any suppression event:
| Header | Description |
|---|---|
X-PMH-FBL-Source-ISP | ISP that originated the complaint (gmail / microsoft / yahoo / apple / custom) |
X-PMH-FBL-Feedback-Type | ARF feedback type (abuse / fraud / dsn) |
X-PMH-FBL-Reported-At | ISO 8601 timestamp when ISP received the complaint |
X-PMH-FBL-Complaint-ID | Unique OmniFBL internal ID for deduplication |
X-PMH-FBL-Processed-At | ISO 8601 timestamp when OmniFBL processed this report |
Deduplication
OmniFBL deduplicates complaints using a SHA-256 hash of the normalised recipient address and ISP source. If the same recipient submits multiple complaints for the same ISP within 24 hours, only the first is processed. Subsequent duplicates are logged with duplicate_suppressed and discarded.
FBL Error Codes
| Code | Meaning | Action |
|---|---|---|
PMH-FBL-001 | Malformed ARF message (missing feedback-type) | Log and discard; increment error counter |
PMH-FBL-002 | Reported domain not verified in PostMTA | Hard block; alert operator |
PMH-FBL-003 | DKIM signature did not verify on reported message | Log; do not suppress; flag for review |
PMH-FBL-004 | ARF parse failure (invalid MIME structure) | Log and discard; increment error counter |
PMH-FBL-005 | ISP source not registered | Reject at MTA envelope stage |