Skip to content

rushikeshsakharleofficial/SRMTA

Repository files navigation

SRMTA

Scalable Reliable Mail Transfer Agent

RFC 5321/5322 compliant MTA built in Go — designed for high-volume transactional and bulk email delivery.

License: MIT Go


What it is

SRMTA is an open-source mail transfer agent written in Go. It handles inbound and outbound SMTP with a focus on deliverability: per-provider throttling, MX-based IP segregation, DKIM signing, bounce classification, SPF validation, and a crash-safe queue. A Node.js admin API and a static dashboard ship alongside the core binary.

Features

Category Capabilities
SMTP Engine ESMTP, STARTTLS (TLS 1.2+), AUTH (PLAIN/LOGIN/CRAM-MD5), pipelining (RFC 2920), DSN, separate inbound :25 and submission :587 ports
Queue 6 spools (incoming → active → deferred → retry → dead-letter → failed), domain bucketing, sharding, crash-safe WAL journal
DNS Async resolver with in-memory caching, MX priority, A/AAAA fallback, resolver pool
IP Pool Health scoring (4xx/5xx/timeout/TLS failures), auto-disable/recovery, warm-up schedules
Smart Throttle Per-provider speed limits (Microsoft, Google, Yahoo, Apple, Zoho, Comcast), adaptive backoff on 4xx, rolling rate windows
MX-Based Routing Provider → dedicated IP segregation (Google, Outlook, Yahoo, Zoho), primary → backup → fallback chains
Sender Routing Bind specific FROM domains or wildcards (*.corp.com) to dedicated IPs or subnets
DKIM Multi-key signing, relaxed canonicalization, per-domain key selection
Bounce Hard/soft/block/policy/mailbox classification, auto-suppression, sender auto-pause
Access Control INI-based allowed_ips.ini and allowed_domains.ini with CIDR ranges and wildcard domains
Compliance FBL webhook ingestion, List-Unsubscribe (RFC 2369 + RFC 8058), SPF validation, DMARC alignment
Observability Prometheus metrics endpoint, structured JSON logging, Grafana dashboard (grafana/dashboard.json)
Admin API Node.js/Fastify REST API with JWT (30-min expiry), RBAC, per-endpoint rate limiting, WebSocket live updates
Config Centralized YAML + config.d/*.yaml sub-configs, environment variable interpolation

Security

SRMTA is security-by-default. The application will not start without required secrets set:

Requirement Details
JWT_SECRET Required. No default. Generate with openssl rand -hex 32
WEBHOOK_SECRET Required. Used for HMAC-SHA256 webhook verification with constant-time comparison
DB_PASSWORD Required. No hardcoded defaults in source or config
CORS Explicit origin allowlist via ALLOWED_ORIGINS (no wildcard)
Auth Database-backed bcrypt authentication. No default credentials
SMTP Auth Default validator rejects all credentials — a real AuthValidator must be injected
TLS STARTTLS handshake failures abort the connection (no plaintext fallback). DB connections default to ssl_mode: require
WebSocket Requires JWT authentication
Rate limiting Global (100/min) + stricter per-endpoint (login: 10/min)
Message IDs Generated with crypto/rand (not math/rand)
Bulk send Capped at 1,000 messages per batch

For vulnerability reporting, see SECURITY_AUDIT.md or open a GitHub Security Advisory.

Prerequisites

Core MTA binary:

  • Go 1.22+
  • PostgreSQL 14+

Admin API (optional, runs separately):

  • Node.js 20+
  • Redis 6+ (used for rate limiting)

Installation

Docker (recommended)

# Copy and fill in required secrets
cp deploy/systemd/srmta.env.example deploy/systemd/srmta.env
vim deploy/systemd/srmta.env   # set JWT_SECRET, WEBHOOK_SECRET, DB_PASSWORD

# Build and start (MTA + admin API + PostgreSQL + Redis)
docker compose up -d

# Tail logs
docker compose logs -f mta

The MTA runs with network_mode: host so it can bind to port 25 and make outbound SMTP connections without NAT. PostgreSQL is initialized automatically from migrations/init_postgres.sql.

Build from source

git clone https://github.com/rushikeshsakharleofficial/SRMTA.git
cd SRMTA
make build
make test
sudo make install

Install from RPM (RHEL / CentOS / Fedora / Rocky)

make rpm
sudo rpm -ivh rpmbuild/RPMS/x86_64/srmta-1.2.0-1.el9.x86_64.rpm

Install from DEB (Debian / Ubuntu)

make deb
sudo dpkg -i debbuild/srmta_1.2.0-1_amd64.deb
sudo apt-get install -f

Getting Started

Post-install setup

# 1. Set required secrets (do this before anything else)
sudo vim /etc/srmta/srmta.env   # JWT_SECRET, WEBHOOK_SECRET, DB_PASSWORD

# 2. Edit main config and sub-configs
sudo vim /etc/srmta/config.yaml
sudo vim /etc/srmta/config.d/10-smtp.yaml      # SMTP ports & limits
sudo vim /etc/srmta/config.d/20-dkim.yaml      # DKIM keys
sudo vim /etc/srmta/config.d/30-ips.yaml       # IP pool
sudo vim /etc/srmta/config.d/40-database.yaml  # DB credentials
sudo vim /etc/srmta/config.d/50-queue.yaml     # Queue & retry policy
sudo vim /etc/srmta/config.d/60-throttle.yaml  # Per-provider speed limits
sudo vim /etc/srmta/config.d/70-routing.yaml   # MX-based IP routing

# 3. Edit access control lists
sudo vim /etc/srmta/allowed_domains.ini
sudo vim /etc/srmta/allowed_ips.ini

# 4. Initialize the database
sudo -u postgres createuser srmta
sudo -u postgres createdb -O srmta srmta
psql -U srmta -d srmta -f /usr/share/srmta/migrations/001_init.sql

# 5. Generate DKIM keys
openssl genrsa -out /etc/srmta/dkim/example.com.key 2048
openssl rsa -in /etc/srmta/dkim/example.com.key -pubout \
  | grep -v "^---" | tr -d '\n'
# → Add the resulting public key as DNS TXT: default._domainkey.example.com

# 6. Start the service
sudo systemctl enable --now srmta.service

# 7. Verify
sudo systemctl status srmta
curl http://localhost:9090/metrics

Configuration

SRMTA uses a layered configuration system:

  1. Main config (/etc/srmta/config.yaml) — base settings
  2. Sub-configs (/etc/srmta/config.d/*.yaml) — merged on top, sorted alphabetically
  3. INI filesallowed_domains.ini, allowed_ips.ini for access control
  4. Environment variables — expand ${VAR} in any YAML value

See configs/config.example.yaml for a fully annotated reference.

SMTP ports

# config.d/10-smtp.yaml
smtp:
  inbound_addr: ":25"     # Receiving (MX traffic)
  submission_addr: ":587" # Authenticated client submission (MSA)
  outbound_port: 25       # Remote port for outbound delivery

Access control

# allowed_domains.ini
[domains]
example.com
.example.com    ; wildcard — all subdomains

# allowed_ips.ini
[ipv4]
203.0.113.10
10.0.0.0/8

[ipv6]
2001:db8::1
2001:db8::/32

Smart throttle (per-provider limits)

Configured in config.d/60-throttle.yaml. Default limits that avoid blacklisting:

Provider Max conn Per sec Per min Per hour Backoff
Microsoft/Outlook 5 3 100 2,000 3×, max 10 min
Google/Gmail 10 10 300 5,000 2.5×, max 8 min
Yahoo/AOL 5 5 150 3,000 2×, max 10 min
Apple/iCloud 3 2 60 1,000 3×, max 15 min
Zoho 5 5 150 3,000 2.5×, max 10 min
Comcast/Xfinity 3 2 50 800 3×, max 15 min
Default (others) 10 20 500 10,000 2×, max 5 min

Rates automatically reduce on 4xx throttle responses and recover on success.

MX-based IP routing

Recipient MX lookup
    │
    ├── *.google.com       → IPs: 203.0.113.10, .11  (backup: .20)
    ├── *.outlook.com      → IPs: 203.0.113.12, .13  (backup: .21)
    ├── *.yahoodns.net     → IPs: 203.0.113.14       (backup: .22)
    ├── *.mail.icloud.com  → IPs: 203.0.113.15       (backup: .23)
    ├── *.zoho.com         → IPs: 203.0.113.19       (backup: .24)
    └── * (all others)     → IPs: 203.0.113.16-.18   (fallback: .50-.51)

Configure in config.d/70-routing.yaml. Sender-domain routes (sender_routes) are evaluated first; unmatched mail falls through to MX-based routing.

Admin API

The Node.js/Fastify API runs independently of the core MTA:

cd web/api
npm install

export JWT_SECRET=$(openssl rand -hex 32)
export WEBHOOK_SECRET=$(openssl rand -hex 32)
export DB_PASSWORD="your-db-password"
export REDIS_URL="redis://localhost:6379"
export ALLOWED_ORIGINS="https://your-admin-domain.com"

npm start   # Listens on :3000

Credentials are stored in the api_users table (bcrypt). All routes require JWT + RBAC (admin / operator / viewer).

Endpoint Auth Description
POST /api/auth/login public Returns JWT (30-min expiry)
GET /api/auth/me JWT Current user info
POST /api/send operator+ Queue a single message
POST /api/bulk operator+ Queue up to 1,000 messages
POST /api/schedule operator+ Schedule a message
GET /api/status/:id operator+ Delivery event status
GET /api/metrics operator+ Aggregated delivery stats (last 24 h)
GET /api/logs operator+ Paginated delivery logs (max 200/page)
GET /api/logs/export operator+ CSV export (max 50,000 rows)
GET /api/queue/stats JWT Queue spool depths
GET /api/ips JWT IP pool health snapshots
POST /api/webhook HMAC FBL/delivery status webhook
GET /ws JWT WebSocket live metrics feed

Full OpenAPI spec: web/api/openapi.yaml

Dashboard

Open web/ui/index.html in a browser (or serve via nginx/Caddy). Connects to the API at the same origin — no hardcoded URLs.

Monitoring

Prometheus

scrape_configs:
  - job_name: srmta
    static_configs:
      - targets: ['mta-host:9090']

Grafana

Import grafana/dashboard.json — pre-built panels for delivery rate, queue depth, active connections, success ratio, IP health, bounces, and latency.

See docs/MONITORING.md for a full observability runbook.

Architecture

┌──────────────┐    ┌────────────┐    ┌───────────────┐    ┌──────────────┐
│  Inbound     │───▶│   Queue    │───▶│   Throttle    │───▶│   Delivery   │
│  SMTP :25    │    │  Manager   │    │   Manager     │    │   Engine     │
│  MSA  :587   │    │  (6 spools)│    │  (per-provider│    │  (worker pool│
└──────────────┘    └────────────┘    │   speed ctrl) │    └──────┬───────┘
                                      └───────────────┘           │
                    ┌─────────────────────────┬──────────────┬─────┘
                    │                         │              │
              ┌─────▼─────┐  ┌──────────┐  ┌─▼────────┐  ┌─▼──────────┐
              │ DNS        │  │ MX-Based │  │ DKIM     │  │ Bounce     │
              │ Resolver   │  │ IP Router│  │ Signer   │  │ Classifier │
              └────────────┘  └──────────┘  └──────────┘  └────────────┘

Repository structure

SRMTA/
├── cmd/
│   ├── srmta/                        # Main MTA binary
│   │   └── main.go
│   └── srmtaq/                       # Queue management CLI
│       └── main.go
│
├── internal/
│   ├── smtp/                         # SMTP engine
│   │   ├── server.go                 # Listener, connection admit, graceful stop
│   │   ├── session.go                # Per-connection state machine (EHLO→DATA)
│   │   ├── client.go                 # Outbound client with connection pooling
│   │   ├── pipeline.go               # RFC 2920 pipelining
│   │   └── ratelimiter.go            # Per-IP connection rate limiting
│   │
│   ├── queue/                        # 6-spool queue
│   │   ├── manager.go                # Enqueue, dispatch, domain sharding
│   │   ├── journal.go                # Crash-safe write-ahead log
│   │   ├── retry.go                  # Retry backoff and classification
│   │   └── scheduler.go              # Deferred and timed delivery
│   │
│   ├── delivery/                     # Outbound worker pool
│   │   ├── engine.go                 # Concurrent delivery orchestration
│   │   └── circuit_breaker.go        # Per-domain failure isolation
│   │
│   ├── routing/
│   │   └── router.go                 # MX-based and sender-domain IP selection
│   │
│   ├── throttle/
│   │   └── manager.go                # Per-provider speed limits, adaptive backoff
│   │
│   ├── dns/
│   │   ├── resolver.go               # Async resolver with in-memory cache
│   │   └── pool.go                   # Resolver pool for concurrency
│   │
│   ├── ip/
│   │   └── pool.go                   # Health scoring, warm-up, auto-disable/recovery
│   │
│   ├── dkim/
│   │   └── signer.go                 # Multi-key signing, per-domain key selection
│   │
│   ├── bounce/
│   │   └── classifier.go             # Hard/soft/block/policy classification, suppression
│   │
│   ├── compliance/
│   │   ├── compliance.go             # FBL, List-Unsubscribe, DMARC alignment
│   │   └── spf.go                    # SPF host check (RFC 7208)
│   │
│   ├── access/
│   │   └── access.go                 # IP/domain ACL (INI allowlists, CIDR)
│   │
│   ├── store/
│   │   └── store.go                  # PostgreSQL + MySQL delivery event store
│   │
│   ├── metrics/
│   │   └── prometheus.go             # Counters, histograms, gauges
│   │
│   ├── logging/
│   │   └── events.go                 # Structured JSON logger with masking
│   │
│   └── config/
│       └── config.go                 # YAML loader, config.d merge, validation
│
├── configs/                          # Config templates (copy to /etc/srmta/)
│   ├── config.example.yaml           # Fully annotated main config
│   ├── config.d/
│   │   ├── 10-smtp.yaml.example      # SMTP ports and limits
│   │   ├── 20-dkim.yaml.example      # DKIM keys
│   │   ├── 30-ips.yaml.example       # IP pool
│   │   ├── 40-database.yaml.example  # DB credentials
│   │   ├── 50-queue.yaml.example     # Queue and retry policy
│   │   ├── 60-throttle.yaml.example  # Per-provider speed limits
│   │   ├── 70-routing.yaml.example   # MX-based IP routing
│   │   └── 75-sender-routing.yaml.example  # Sender-domain → IP binding
│   ├── allowed_domains.ini.example
│   └── allowed_ips.ini.example
│
├── migrations/
│   ├── init_postgres.sql
│   └── init_mysql.sql
│
├── deploy/
│   ├── systemd/                      # srmta.service, srmta.socket, srmta.env.example
│   ├── rpm/                          # srmta.spec
│   └── deb/                          # debian/ packaging (control, changelog, rules)
│
├── web/
│   ├── api/                          # Node.js / Fastify admin API
│   │   ├── src/server.js
│   │   ├── Dockerfile
│   │   ├── openapi.yaml
│   │   └── package.json
│   └── ui/
│       └── index.html                # Static admin dashboard (no build step)
│
├── grafana/
│   └── dashboard.json                # Pre-built 12-panel Grafana dashboard
│
├── Dockerfile                        # Multi-stage build: Go binary → alpine runtime
├── docker-compose.yml                # MTA + admin API + PostgreSQL + Redis
└── docs/
    ├── MONITORING.md
    ├── PRODUCTION_CHECKLIST.md
    ├── SCALING.md
    └── SECURITY_AUDIT.md

Make targets

Command Description
make build Compile ./srmta (CGO disabled, version embedded)
make test Run all tests with race detector (go test -race ./...)
make bench Run benchmarks with memory stats
make lint Run golangci-lint
make install Install binary, systemd units, config templates
make rpm Build RPM package
make deb Build Debian package
make clean Remove build artifacts

Contributing

Contributions are welcome when they add real value. Low-effort PRs (trivial renames, comment tweaks, style-only changes with no substance) will be closed without review.

Before opening a PR:

  1. Open an issue first for anything non-trivial — especially new features, architecture changes, or anything in the "areas where help is needed" list. This prevents duplicated effort.
  2. Fork the repository and create a branch from master:
    git checkout -b feat/your-feature-name
  3. Follow Conventional Commits (feat, fix, perf, test, docs, etc.) with the relevant scope (smtp, queue, compliance, config, api, ui).
  4. Ensure the PR checklist passes before requesting review:
    • make build — compiles without errors
    • make test — all tests pass (race detector enabled)
    • make lint — no new linter findings
    • New code has tests
    • No breaking changes (or clearly documented)

Areas where contributions are especially useful:

  • DMARC record lookup — DNS-based policy evaluation in inbound compliance checks
  • SPF hardening — proper nested include, redirect, and macro expansion per RFC 7208
  • Test coverageinternal/smtp, internal/queue, internal/compliance, and internal/config have gaps
  • Admin UI modernization — migrate the static HTML dashboard to React or Vue
  • Alpine / Arch packagingAPKBUILD and PKGBUILD to complement the existing RPM and DEB
  • Documentation — architecture diagrams, platform deployment guides, expanded API reference

See CONTRIBUTING.md for the full workflow and CODE_OF_CONDUCT.md for community standards.

License

This project is licensed under the MIT License. See LICENSE for details.

About

Scalable Reliable Mail Transfer Agent — high-performance MTA in Go with per-domain queues, DKIM/SPF/DMARC, bounce handling, rate limiting, and web dashboard. Modern alternative to Postfix and Exim.

Topics

Resources

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages