Asynchronous email dispatcher microservice with scheduling, rate limiting, attachments, and a FastAPI REST API.
Project description
genro-mail-proxy
Asynchronous email dispatcher microservice with scheduling, rate limiting, attachments (S3/URL/base64), REST API (FastAPI), and Prometheus metrics.
Why Use an Email Proxy?
Instead of directly connecting to SMTP servers from your application, genro-mail-proxy provides a decoupled, resilient email delivery layer with:
- ⚡ 19x faster requests (32ms vs 620ms) - non-blocking async operations
- 🔄 Never lose messages - automatic retry, guaranteed persistence
- 🎯 Connection pooling - 10-50x faster for burst sends
- 📊 Centralized monitoring - Prometheus metrics, not scattered logs
- 🛡️ Built-in rate limiting - shared across all app instances
- 🎛️ Priority queuing - immediate/high/medium/low with automatic ordering
See Architecture Overview for detailed comparison with direct SMTP.
Main integration points:
- REST control plane secured by
X-API-Tokenfor queue management and configuration. - Outbound
proxy_synccall towards Genropy, authenticated via basic auth and configured through[client]inconfig.ini. - Delivery reports and Prometheus metrics to monitor message lifecycle and rate limiting.
- Unified SQLite storage with a single
messagestable that tracks queue state (priority,deferred_ts) and delivery lifecycle (sent_ts,error_ts,reported_ts). - Background loops:
- SMTP dispatch loop selects records from
messagesthat lacksent_ts/error_tsand havedeferred_tsin the past, enforces rate limits, then stampssent_tsorerror_ts/error. - Client report loop batches completed items (sent/error/deferred) that are still missing
reported_tsand posts them to the upstreamproxy_syncendpoint; on acknowledgement the records receivereported_tsand are later purged according to the retention window.
- SMTP dispatch loop selects records from
Quick start
docker build -t genro-mail-proxy .
docker run -p 8000:8000 \
-e GMP_CLIENT_SYNC_URL=https://your-app/proxy_sync \
-e GMP_CLIENT_SYNC_USER=syncuser \
-e GMP_CLIENT_SYNC_PASSWORD=syncpass \
-e GMP_API_TOKEN=your-secret-token \
genro-mail-proxy
See config.ini.example for all available environment variables (all prefixed with GMP_).
Example client
A complete integration example is provided in example_client.py. This demonstrates the recommended pattern for integrating with the mail service:
# Install dependencies
pip install fastapi uvicorn aiohttp
# Configure your email address
nano example_config.ini # Edit recipient_email
# Start the example client
python3 example_client.py
# Send test email
curl -X POST http://localhost:8081/send-test-email
The example shows:
- Local-first persistence (never lose messages)
- Async submission to mail service
- run-now trigger for fast delivery
- Delivery report handling via proxy_sync
See Example Client Documentation for detailed walkthrough.
Configuration highlights
[delivery]exposesdelivery_report_retention_secondsto control how long reported messages stay in themessagestable (default seven days)./commands/add-messagesvalidates each payload (id,from,toetc.), enqueues valid messages withpriority=2when omitted, and returns a response with queued count plus arejectedlist containing{"id","reason"}entries for failures.
Attachment Handling
genro-mail-proxy supports multiple attachment sources with flexible routing.
Storage Path Formats
| Format | Example | Description |
|---|---|---|
base64:content |
base64:SGVsbG8= |
Inline base64-encoded content |
/absolute/path |
/tmp/attachments/file.pdf |
Local filesystem absolute path |
relative/path |
uploads/doc.pdf |
Filesystem relative to configured base_dir |
@params |
@doc_id=123&version=2 |
HTTP POST to default endpoint |
@[url]params |
@[https://api.example.com]id=456 |
HTTP POST to specific URL |
Attachment Configuration
[attachments]
# Filesystem fetcher base directory (for relative paths)
base_dir = /var/attachments
# HTTP fetcher default endpoint
http_endpoint = https://api.example.com/attachments
http_auth_method = bearer # none, bearer, or basic
http_auth_token = your-secret-token
# For basic auth:
# http_auth_user = username
# http_auth_password = password
Attachment Cache
A two-tiered cache (memory + disk) reduces redundant fetches for frequently used attachments.
MD5 Cache Marker
Filenames can include an MD5 hash marker for cache lookup:
report_{MD5:a1b2c3d4e5f6}.pdf
The marker is extracted for cache lookup and removed from the final filename.
Cache Configuration
[attachments]
# Memory cache (LRU)
cache_memory_max_items = 100
cache_memory_ttl_seconds = 3600
# Disk cache (optional, for large files)
cache_disk_dir = /var/cache/mail-proxy
cache_disk_ttl_seconds = 86400
# Files larger than this threshold use disk cache
cache_memory_max_size_bytes = 1048576 # 1MB
Cache Behavior
- Small files (<
cache_memory_max_size_bytes): stored in memory LRU cache - Large files: stored on disk if
cache_disk_diris configured - Cache lookup: if filename contains
{MD5:hash}, cache is checked before fetching - Cache population: after fetch, content is cached using computed MD5
Development
pip install -e ".[dev]"
pytest
License
Apache License 2.0 — see LICENSE for 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.3.5.tar.gz.
File metadata
- Download URL: genro_mail_proxy-0.3.5.tar.gz
- Upload date:
- Size: 162.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc35ff9a45892d873ea8e346589537c4cf79c74896dea534f4ac281e51a49de9
|
|
| MD5 |
d619facd4c23c41a619e2b1035c7bc67
|
|
| BLAKE2b-256 |
3854170b25c381cb04a46850f24bb97ff8efecd9e24d86cb33f7a7f234c048d7
|
Provenance
The following attestation bundles were made for genro_mail_proxy-0.3.5.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.3.5.tar.gz -
Subject digest:
fc35ff9a45892d873ea8e346589537c4cf79c74896dea534f4ac281e51a49de9 - Sigstore transparency entry: 844669000
- Sigstore integration time:
-
Permalink:
genropy/genro-mail-proxy@3ffa6e6cc920fb7b4f46decef123d4d4ae0578bb -
Branch / Tag:
refs/tags/v0.3.5 - Owner: https://github.com/genropy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3ffa6e6cc920fb7b4f46decef123d4d4ae0578bb -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file genro_mail_proxy-0.3.5-py3-none-any.whl.
File metadata
- Download URL: genro_mail_proxy-0.3.5-py3-none-any.whl
- Upload date:
- Size: 93.5 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 |
439f1185e82c7013a395a79029e789870661be32235e7f1948538fcedcd4f7ee
|
|
| MD5 |
faa4e4e10139066787cda1637e76f782
|
|
| BLAKE2b-256 |
bfee69428905a690e34ec59ffcbcd62e9adca13dccd3887aa81d3d29070d0fad
|
Provenance
The following attestation bundles were made for genro_mail_proxy-0.3.5-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.3.5-py3-none-any.whl -
Subject digest:
439f1185e82c7013a395a79029e789870661be32235e7f1948538fcedcd4f7ee - Sigstore transparency entry: 844669002
- Sigstore integration time:
-
Permalink:
genropy/genro-mail-proxy@3ffa6e6cc920fb7b4f46decef123d4d4ae0578bb -
Branch / Tag:
refs/tags/v0.3.5 - Owner: https://github.com/genropy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3ffa6e6cc920fb7b4f46decef123d4d4ae0578bb -
Trigger Event:
workflow_dispatch
-
Statement type: