Asynchronous email dispatcher microservice with scheduling, rate limiting, attachments, and a FastAPI REST API.
Project description
genro-mail-proxy
A microservice that decouples email delivery from your application.
What it does
genro-mail-proxy sits between your application and SMTP servers. Your application sends messages to the proxy via REST API; the proxy handles delivery with:
- Persistent queue: Messages are stored in SQLite or PostgreSQL and survive restarts
- Automatic retry: Failed deliveries are retried with exponential backoff
- Rate limiting: Per-account limits (minute/hour/day) shared across instances
- Priority queuing: Four levels (immediate, high, medium, low) with FIFO within each
- Delivery reports: Results are posted back to your application via HTTP callback
- Bounce detection: IMAP polling for bounces with DSN parsing and hard/soft classification (BSL 1.1)
- Multi-tenancy: Multiple organizations can share one instance with separate accounts (BSL 1.1)
- PEC support: Italian certified email (Posta Elettronica Certificata) with receipt tracking (BSL 1.1)
- Large file handling: Auto-upload attachments to S3/GCS/Azure and replace with download links (BSL 1.1)
- Connection pooling: SMTP connections are pooled with acquire/release semantics
┌─────────────┐ REST ┌──────────────────┐ SMTP ┌─────────────┐
│ Application │ ──────────────► │ genro-mail-proxy │ ─────────────► │ SMTP Server │
└─────────────┘ └──────────────────┘ └─────────────┘
▲ │
│ │
└───────────────────────────────┘
delivery reports
When to use it
Consider this proxy when:
- Multiple application instances need shared rate limits for outbound email
- Email delivery should not block your application's main request flow
- Delivery tracking is needed with central logging and Prometheus metrics
- Retry logic is required without implementing it in every service
- Multi-tenant isolation is needed for different organizations or environments
When NOT to use it
This proxy adds operational complexity. Direct SMTP may be simpler when:
- You have a single application instance with low email volume
- Latency is acceptable (direct SMTP adds ~500-600ms per send)
- No retry logic is needed (transactional emails with immediate feedback)
- No rate limiting is required by your SMTP provider
- You prefer fewer moving parts in your infrastructure
Quick start
Docker:
docker run -p 8000:8000 \
-e GMP_API_TOKEN=your-secret-token \
genro-mail-proxy
CLI:
pip install genro-mail-proxy
mail-proxy start myserver
Then configure a tenant, add an SMTP account, and start sending messages.
Command-line interface
The mail-proxy CLI manages instances without going through the HTTP API:
# Instance management
mail-proxy list # List all instances
mail-proxy start myserver # Start an instance
mail-proxy stop myserver # Stop an instance
mail-proxy myserver info # Show instance details
# Tenant management
mail-proxy myserver tenants list # List tenants
mail-proxy myserver tenants add acme # Add a tenant (interactive)
# Account management (per tenant)
mail-proxy myserver acme accounts list # List SMTP accounts
mail-proxy myserver acme accounts add # Add account (interactive)
# Message operations
mail-proxy myserver acme messages list # List queued messages
mail-proxy myserver acme send email.eml # Send from .eml file
mail-proxy myserver acme run-now # Trigger immediate dispatch
Each instance stores its configuration in ~/.mail-proxy/<name>/mail_service.db.
The CLI supports both command-line arguments and interactive prompts for complex operations.
REST API
The proxy exposes a FastAPI REST API secured by X-API-Token:
POST /commands/add-messages- Queue messages for deliveryGET /messages- List queued messagesPOST /commands/run-now- Trigger immediate dispatch cycleGET /accounts- List SMTP accountsGET /metrics- Prometheus metrics
See API Reference for details.
Attachment handling
The proxy supports multiple attachment sources via explicit fetch_mode:
| fetch_mode | storage_path example | Description |
|---|---|---|
base64 |
base64:SGVsbG8gV29ybGQ= |
Inline base64-encoded content (requires base64: prefix) |
filesystem |
/tmp/file.pdf |
Local filesystem path |
endpoint |
doc_id=123 |
HTTP POST to tenant's attachment endpoint |
http_url |
https://storage.example.com/file.pdf |
HTTP GET from external URL |
A two-tiered cache (memory + disk) reduces redundant fetches. Filenames can include an MD5 hash marker (report_{MD5:abc123}.pdf) for cache lookup.
Large file offloading
For attachments exceeding a size threshold, the proxy can upload them to external storage (S3, GCS, Azure, or local filesystem) and replace them with download links in the email body.
pip install genro-mail-proxy[large-files]
Configure per-tenant via large_file_config:
{
"enabled": true,
"max_size_mb": 10,
"storage_url": "s3://bucket/mail-attachments",
"action": "rewrite"
}
Actions: warn (log only), reject (fail message), rewrite (upload and replace with link).
Configuration
Configuration is managed via the CLI. Each instance stores its settings in ~/.mail-proxy/<name>/mail_service.db.
# Start an instance (creates it if new)
mail-proxy start myserver
# Add a tenant with attachment endpoint
mail-proxy myserver tenants add
# Interactive prompts: tenant_id, name, base_url, attachment_path, auth method
# Add an SMTP account
mail-proxy myserver acme accounts add
# Interactive prompts: account_id, host, port, user, password, TLS, rate limits
For Docker deployments, use environment variables (prefixed with GMP_):
docker run -p 8000:8000 \
-e GMP_API_TOKEN=your-secret-token \
-e GMP_DB_PATH=/data/mail_service.db \
-v mail-data:/data \
genro-mail-proxy
See Usage for all options.
Performance notes
- Request latency: ~30ms to queue a message (vs ~600ms for direct SMTP)
- Throughput: Limited by SMTP provider rate limits, not the proxy
- Memory: Attachment content is held in memory during send; use HTTP endpoints for large files
The SQLite database handles typical workloads but doesn't scale well under high concurrency. For high-volume deployments, use PostgreSQL:
pip install genro-mail-proxy[postgresql]
Then configure the DSN via environment variable or CLI.
Development
pip install -e ".[dev]"
pytest
License
This project uses a dual-license model:
| License | Features |
|---|---|
| Apache 2.0 | Core functionality: message queue, retry, rate limiting, priority, delivery reports, attachments, SMTP pooling, REST API, CLI |
| BSL 1.1 | Multi-Tenancy, Bounce Detection, PEC Support, Large Files |
Apache 2.0 (LICENSE): Free for any use.
BSL 1.1 (LICENSE-BSL-1.1): Free for testing, development, and non-production. Production use requires a commercial license from Softwell S.r.l. After 2030-01-25, these features convert to Apache 2.0.
See NOTICE for full details.
Copyright 2025 Softwell S.r.l. — Genropy Team
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file genro_mail_proxy-0.6.9.tar.gz.
File metadata
- Download URL: genro_mail_proxy-0.6.9.tar.gz
- Upload date:
- Size: 2.0 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
851a9a3771fd9a58f0c2e5e62ecc8081d2be388d6594a0afc568db66453c4302
|
|
| MD5 |
31cff9e14a25170817ab6058d3bc4189
|
|
| BLAKE2b-256 |
ca69be86b2716429e3140f5309c3d124081fb67f1bc8dd29971919bb5c0e9c31
|
Provenance
The following attestation bundles were made for genro_mail_proxy-0.6.9.tar.gz:
Publisher:
publish.yml on genropy/genro-mail-proxy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
genro_mail_proxy-0.6.9.tar.gz -
Subject digest:
851a9a3771fd9a58f0c2e5e62ecc8081d2be388d6594a0afc568db66453c4302 - Sigstore transparency entry: 863050887
- Sigstore integration time:
-
Permalink:
genropy/genro-mail-proxy@dec89b7de476dfc9c7470358767b7029cd0e0921 -
Branch / Tag:
refs/tags/v0.6.9 - Owner: https://github.com/genropy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dec89b7de476dfc9c7470358767b7029cd0e0921 -
Trigger Event:
push
-
Statement type:
File details
Details for the file genro_mail_proxy-0.6.9-py3-none-any.whl.
File metadata
- Download URL: genro_mail_proxy-0.6.9-py3-none-any.whl
- Upload date:
- Size: 178.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb4002eabae96ff1a3124c3e7c7ff4d5d60ab5ebb597d86f122a5cf020579919
|
|
| MD5 |
c0b787a5a97b526a6f30795a95c87040
|
|
| BLAKE2b-256 |
672336f48f535f142aa5cbf680569e21ae2574604fcec8826d71b5f2e687e31f
|
Provenance
The following attestation bundles were made for genro_mail_proxy-0.6.9-py3-none-any.whl:
Publisher:
publish.yml on genropy/genro-mail-proxy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
genro_mail_proxy-0.6.9-py3-none-any.whl -
Subject digest:
cb4002eabae96ff1a3124c3e7c7ff4d5d60ab5ebb597d86f122a5cf020579919 - Sigstore transparency entry: 863050888
- Sigstore integration time:
-
Permalink:
genropy/genro-mail-proxy@dec89b7de476dfc9c7470358767b7029cd0e0921 -
Branch / Tag:
refs/tags/v0.6.9 - Owner: https://github.com/genropy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dec89b7de476dfc9c7470358767b7029cd0e0921 -
Trigger Event:
push
-
Statement type: