English | 日本語
DMARC and MTA-STS report viewer
graph TD
subgraph IMAP Servers
S1[IMAP Server<br>Gmail, etc.]
S2[IMAP Server<br>Work, etc.]
end
subgraph reports
IMAP[IMAP Client<br><i>libcurl</i>]
MIME[MIME Parser<br><i>base64 decode</i>]
DECOMP[Decompress<br><i>gzip/zip via zlib</i>]
DMARC[DMARC Parser<br><i>libxml2</i>]
MTASTS[MTA-STS Parser<br><i>std.json</i>]
STORE[(Store<br>JSON files per account)]
CLI[CLI / C ABI<br>fetch, list, show, summary]
end
S1 -- IMAPS --> IMAP
S2 -- IMAPS --> IMAP
IMAP --> MIME --> DECOMP
DECOMP --> DMARC
DECOMP --> MTASTS
DMARC --> STORE
MTASTS --> STORE
STORE --> CLI
CLI --> Terminal[Terminal<br>table output]
CLI --> JSON[JSON stdout<br>--format json]
CLI --> Swift[SwiftUI macOS<br>via libreports-core.a]
- Fetch DMARC aggregate reports (RFC 7489) and TLS-RPT reports (RFC 8460) from IMAP
- Multiple IMAP account support with per-account storage
- Parse XML/JSON report formats with ZIP/GZIP decompression
- List, show, and summarize reports with table or JSON output
- Filter by account, domain, and time period (week/month/year)
- Incremental fetch with UID tracking (skips already-fetched messages)
- Parallel IMAP fetch with multiple connections, auto-scaled to CPU cores (~3.5x faster)
- Headless core with C ABI static library for native UI integration
Requires Zig 0.15.2 or later.
$ git clone https://github.com/linyows/reports.git
$ cd reports
$ zig build --release=fastThe binary will be available at ./zig-out/bin/reports.
- libxml2 - DMARC XML parsing
- libcurl - IMAP connectivity
- zlib - gzip/zip decompression
On macOS, these are included in the SDK. On Linux:
$ sudo apt-get install libxml2-dev libcurl4-openssl-dev zlib1g-devCreate ~/.config/reports/config.json:
{
"accounts": [
{
"name": "personal",
"host": "imap.gmail.com",
"port": 993,
"username": "[email protected]",
"password": "your-app-password",
"mailbox": "INBOX",
"tls": true
}
]
}For Gmail, generate an App Password. Set mailbox to the label name if reports are filtered (e.g., "dmarc").
Legacy single-account format ("imap": {...}) is also supported and treated as a "default" account.
$ reports fetch
$ reports fetch --account personal
$ reports fetch --full # re-fetch all messagesMessages are fetched in parallel using multiple IMAP connections (auto-scaled to CPU cores). Subsequent runs skip already-fetched messages via UID tracking.
$ reports list
ACCOUNT TYPE ORGANIZATION REPORT ID DATE DOMAIN
---------- -------- -------------------- ------------------------------ ----------------- --------------------
personal DMARC google.com 12864733003343132926 2026-04-02 00:00 example.com
personal DMARC google.com 3504435274969495050 2026-04-01 00:00 example.com
...
$ reports list --account personal --domain example.com
$ reports list --format json$ reports show 12864733003343132926
Organization: google.com
Report ID: 12864733003343132926
Domain: example.com
Policy: none
SOURCE IP COUNT DISPOSITION ENVELOPE FROM HEADER FROM DKIM SPF
---------------- ------ ------------ ------------------------- ------------------------- ------ ------
198.51.100.1 4 none example.com fail pass
$ reports show 12864733003343132926 --format json$ reports summary --format table
DMARC Reports: 186
TLS-RPT Reports: 0
Total Messages: 547
DKIM/SPF Pass: 182
DKIM/SPF Fail: 365
$ reports summary --period month --format table
$ reports summary --account personal --domain example.com --format json$ reports domains
$ reports domains --format jsonThe fetch command uses parallel IMAP connections to download messages concurrently. The number of workers is automatically determined based on CPU core count (up to 16).
| Mode | 224 messages |
|---|---|
| Sequential (single connection) | 1m 54s |
| Parallel (auto-scaled workers) | 33s |
Incremental UID tracking ensures that subsequent runs only fetch new messages, making regular use nearly instant.
The build produces a static library and C header for native app integration:
$ zig build
$ ls zig-out/lib/libreports-core.a
$ ls zig-out/include/reports.h#include "reports.h"
reports_init();
char *json = reports_list(config_json);
// use json...
reports_free_string(json);
reports_deinit();| Tool | Language | UI | DMARC | TLS-RPT | IMAP Fetch | No Infra Required |
|---|---|---|---|---|---|---|
| Reports | Zig + Swift | CLI + macOS native | Yes | Yes | Yes (parallel) | Yes |
| parsedmarc | Python | CLI + Elasticsearch/Grafana | Yes | No | Yes | No |
| dmarc-report-viewer | Rust | Web (single binary) | Yes | Yes | Yes | Yes |
| dmarcguard | Go + Vue.js | Web (single binary) | Yes | No | Yes | Yes |
| dmarc-visualizer | Docker | Web (Grafana) | Yes | No | No | No |
| dmarc-report-converter | Go | CLI | Yes | No | No | Yes |
| dmarc-srg | PHP | Web | Yes | No | No | No |
| Viesti-Reports | PHP | Web | Yes | Yes | No | No |
Key differentiators of Reports:
- Native macOS app -- The only open-source DMARC/TLS-RPT tool with a native desktop GUI (SwiftUI), not just a web UI or CLI
- Headless core architecture -- Zig-based core compiled to a C ABI static library, enabling both CLI and native UI from a single codebase (inspired by Ghostty)
- Parallel IMAP fetch -- Multi-threaded worker pool auto-scaled to CPU cores, ~3.5x faster than sequential fetch
- Zero external infrastructure -- No database, web server, or Docker required; just a config file and local JSON storage
- DNS-based IP enrichment -- PTR, ASN, organization, and country lookup via DNS queries (Team Cymru) without external API keys or GeoIP databases
- Monitoring integration --
checkcommand with exit codes (0/1/2) for anomaly detection, designed for cron jobs and monitoring systems
# Build
zig build
# Run tests
zig build test
# Format check
zig fmt --check src/
# Run
zig build run -- help