E-mail¶
Architecture¶
Internet → Traefik → hosting-postfix (SMTP)
→ hosting-dovecot (IMAP + OAuth2)
→ hosting-rspamd (Spam-Filter)
Platform API → IMAP Proxy (aioimaplib) → Dovecot
→ SMTP Service (aiosmtplib) → Postfix
→ Mail-Watcher (IMAP IDLE) → WebSocket Events
| Component | Image | Port | Description |
|---|---|---|---|
| Postfix | alpine: 3.19 | 25, 465, 587 | SMTP (MX, SMTPS, Submission) |
| Dovecot | xynap/dovecot-oauth2:2.3.21 | 993 | IMAPS + OAuth2 (Custom Build) |
| Rspamd | - | 11332 | Milter fuer Postfix |
| Platform API | - | 8099 | Webmail Proxy, Watcher, Signatures |
Dovecot Docker image¶
Custom build from Source (Alpine package has no OAuth2 passdb driver):
- Dockerfile:
/etc/xynap/hosting/dovecot/Dockerfile - Multi-Stage:Builder (compile) → Runtime (alpine:3.19 + openssl + libcurl + mariadb-connector-c)
- Auth-Mechanisms:
plain login oauthbearer xoauth2(password + OAuth2 parallel)
Authentication¶
All mail endpoints accept two Auth methods :
| Method | Header | Scope |
|---|---|---|
| Service tokens | x-service-token: <token> |
Platform (all domains/accounts visible) |
| Bearer Token | Authorization: Bearer <jwt> |
Tenant-Scope (only own data) |
Service tokens Example:
Mail administration API¶
Domains¶
# Domains auflisten
GET /api/v1/mail/domains
# Domain erstellen (erstellt auch DNS-Records: MX, SPF, DMARC, DKIM)
POST /api/v1/mail/domains
{"domain": "example.com", "customer_id": 1}
Mailboxes¶
# Mailboxen einer Domain
GET /api/v1/mail/domains/{id}/accounts
# Mailbox erstellen
POST /api/v1/mail/domains/{id}/accounts
{"local_part": "info", "password": "...", "quota_mb": 1024}
# Passwort aendern
PUT /api/v1/mail/accounts/{id}
{"password": "neues-passwort"}
# Mailbox sperren/entsperren
POST /api/v1/mail/accounts/{id}/suspend
POST /api/v1/mail/accounts/{id}/unsuspend
Aliase¶
# Aliase einer Domain
GET /api/v1/mail/domains/{id}/aliases
# Alias erstellen
POST /api/v1/mail/domains/{id}/aliases
{"source": "sales", "destination": "info@example.com"}
Webmail API¶
Nativer IMAP/SMTP proxy — replaced Roundcube.
Folder & News¶
# Ordnerliste mit Unread-Count
GET /api/v1/mail/webmail/folders
# Nachrichten eines Ordners (paginiert)
GET /api/v1/mail/webmail/messages/{folder}?page=1&per_page=50
# Einzelne Nachricht (HTML Body)
GET /api/v1/mail/webmail/messages/{folder}/{uid}
# Nachricht loeschen (in Trash verschieben)
DELETE /api/v1/mail/webmail/messages/{folder}/{uid}
# Nachricht verschieben
POST /api/v1/mail/webmail/messages/{folder}/{uid}/move
{"target": "Archive"}
# Flags setzen (Gelesen, Stern, etc.)
POST /api/v1/mail/webmail/messages/{folder}/{uid}/flags
{"flags": ["\\Seen", "\\Flagged"]}
Send¶
# Mail senden (HTML + Plain-Text)
POST /api/v1/mail/webmail/send
{
"to": ["empfaenger@example.com"],
"cc": [],
"bcc": [],
"subject": "Betreff",
"html": "<p>HTML Body</p>",
"plain": "Plain-Text Fallback",
"in_reply_to": "<message-id>" # optional, fuer Reply
}
Signatures¶
# Signaturen auflisten
GET /api/v1/mail/signatures/
# Signatur erstellen
POST /api/v1/mail/signatures/
{
"name": "Standard",
"html": "<p>Mit freundlichen Gruessen</p>",
"is_default": true,
"rules": [
{"match_type": "domain", "match_value": "example.com"}
]
}
# Passende Signatur fuer Empfaenger
GET /api/v1/mail/signatures/match?to=user@example.com
Contact Autocomplete¶
Real-time events (Mail-Watcher)¶
The mail shaker monitors Postfaecher via IMAP IDLE and sends events via WebSocket.
Methods (Auto-Detect per Server)¶
| Method | Description | Priority |
|---|---|---|
| IMAP IDLE | Server push for new messages | 1 (prima) |
| Dovecot Push | HTTP Webhook from the mail server | 2 |
| Pollening | STATUS check every 60 seconds | 3 (caseback) |
WebSocket Events¶
// Neue Nachricht
{"type": "mail.new_message", "data": {
"folder": "INBOX",
"message": {"uid": 42, "from_addr": "sender@example.com", "subject": "..."}
}}
// Ordner-Update (Unread-Count)
{"type": "mail.folder_update", "data": {
"folder": "INBOX",
"unseen": 5,
"exists": 42
}}
Watcher status¶
Mail OAuth2 (XOAUTH2 / OAUTHBEARER)¶
Platform API acts as OAuth2 Authorization Server for the mail stack. External IMAP/SMTP clients (Thunderbird, Apple Mail, etc.) can authenticate themselves via OAuth2 token.
Flow¶
1. User Login → POST /api/v1/auth/login → JWT (type=access)
2. Mail-Token → POST /api/v1/auth/mail-token → JWT (type=mail_access, 60 Min)
3. IMAP Connect → AUTHENTICATE XOAUTH2 <token> → Dovecot
4. Introspection → Dovecot POST /introspect → Platform API validiert Token
5. Login OK → IMAP Session aktiv
Request tokens¶
# Mail-OAuth2-Token fuer eingeloggten User (erfordert aktives Mail-Konto)
POST /api/v1/auth/mail-token
Authorization: Bearer <access_token>
# Optional: spezifisches Mail-Konto angeben
POST /api/v1/auth/mail-token
{"email": "info@example.com"}
Response:
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"email": "user@example.com"
}
Token Introspection (RFC 7662)¶
Internal — Dovecot calls this endpoint to validate tokens:
Response:
{
"active": true,
"username": "user@example.com",
"scope": "imap smtp",
"token_type": "Bearer",
"exp": 1773178446
}
Dovecot configuration¶
# auth_mechanisms (parallel: Passwort + OAuth2)
auth_mechanisms = plain login oauthbearer xoauth2
# OAuth2 passdb (Token Introspection via Platform API)
passdb {
driver = oauth2
mechanisms = xoauth2 oauthbearer
args = /etc/dovecot/auth-oauth2.conf.ext
}
auth-oauth2.conf.ext:
introspection_url = http://platform-api:8099/api/v1/auth/token/introspect
introspection_mode = post
username_attribute = username
active_attribute = active
active_value = true
IMAP client example (XOAUTH2)¶
import base64, imaplib
user = "user@example.com"
token = "<mail_access JWT>"
# XOAUTH2 SASL String
auth_string = f"user={user}\x01auth=Bearer {token}\x01\x01"
auth_b64 = base64.b64encode(auth_string.encode()).decode()
imap = imaplib.IMAP4_SSL("mail.xynap.tech", 993)
imap.authenticate("XOAUTH2", lambda x: auth_string.encode())
imap.select("INBOX")
DNS records (automatic)¶
For domain creation, the following records are created:
| Type | Name | Value |
|---|---|---|
| MX | @ | nexus.isn-systems.com(Prio 10) |
| TXT | @ | v=spf1 ip4:46.4.96.105 -all |
| TXT | dmarc | v=DMARC1; p=quarantine; rua=... |
| TXT | mail. domainkey | DKIM Public Key |
| CNAME | autoconfig | nexus.isn-systems.com |
Mail storage¶
/var/lib/xynap/mail/
├── vhosts/{domain}/{user}/ # Maildir
├── dkim/ # DKIM-Keys
└── ssl/ # SSL-Zertifikate
vmail User: UID/GID 5000