- Complete Mail Server: MTA, MDA, MSA etc.
- Calendar Sync via CalDav
- Contacts Sync via CardDav
- Transactional Email API via GraphQL
- HTTP Server (GraphQL API + WebDav / CalDav / CardDav etc.)
- SMTP Server (25) - Mail Transfer (Incoming)
- SMTP Server (587) - Mail Submission (Outgoing)
- IMAP Server
The server now loads SMTP and mail-auth configuration from environment variables:
SERVER_HOSTNAME=mail.example.com
SERVER_BIND_ADDR=0.0.0.0
SMTP_TRANSFER_PORT=25
SMTP_SUBMISSION_PORT=587
SMTP_OUTBOUND_PORT=25
SMTP_OUTBOUND_REQUIRE_STARTTLS=false
SMTP_OUTBOUND_ALLOW_INVALID_CERTS=false
SMTP_OUTBOUND_ALLOW_PLAINTEXT_FALLBACK=true
SMTP_OUTBOUND_TLS_MODE=required
SMTP_OUTBOUND_DANE_MODE=prefer
SMTP_OUTBOUND_DNSSEC_VALIDATE=true
SMTP_OUTBOUND_MTA_STS_ENFORCE=true
SMTP_TLS_REPORT_DIR=data/tlsrpt
SMTP_OUTBOUND_TIMEOUT_SECS=30
SMTP_OUTBOUND_QUEUE_ENABLED=true
SMTP_OUTBOUND_QUEUE_POLL_INTERVAL_SECS=2
SMTP_OUTBOUND_QUEUE_CLEANUP_INTERVAL_SECS=300
SMTP_OUTBOUND_QUEUE_BATCH_SIZE=100
SMTP_OUTBOUND_QUEUE_LEASE_SECS=120
SMTP_OUTBOUND_RETRY_BASE_DELAY_SECS=30
SMTP_OUTBOUND_RETRY_MAX_DELAY_SECS=3600
SMTP_OUTBOUND_MAX_ATTEMPTS=10
SMTP_OUTBOUND_TTL_SECS=86400
SMTP_OUTBOUND_DEAD_JOB_RETENTION_SECS=604800
SMTP_OUTBOUND_DELIVERY_RETENTION_SECS=2592000
SMTP_INBOUND_MAX_RCPT_TO=100
SMTP_MAX_MESSAGE_BYTES=26214400
SMTP_RATE_LIMIT_ENABLED=true
SMTP_RATE_LIMIT_WINDOW_SECS=60
SMTP_RATE_LIMIT_TRANSFER_CONNECTIONS_PER_WINDOW=240
SMTP_RATE_LIMIT_SUBMISSION_CONNECTIONS_PER_WINDOW=120
SMTP_RATE_LIMIT_TRANSFER_MESSAGES_PER_WINDOW=240
SMTP_RATE_LIMIT_SUBMISSION_MESSAGES_PER_WINDOW=120
SMTP_RATE_LIMIT_FAIL_OPEN_ON_DB_ERROR=true
SMTP_REQUIRE_SPF_PASS=true
SMTP_REQUIRE_DKIM_PASS=false
SMTP_REQUIRE_DMARC_PASS=false
SMTP_AUTH_ALLOW_HEADER_OVERRIDE=false
# Optional DKIM signing for outbound submission (PKCS8 RSA private key in base64)
SMTP_DKIM_DOMAIN=example.com
SMTP_DKIM_SELECTOR=mail
SMTP_DKIM_PRIVATE_KEY_B64_PKCS8=
SMTP_DKIM_HEADERS=From,To,Subject,Date
# Optional S3-compatible object storage (Cloudflare R2 via S3 API)
S3_ENDPOINT=https://<accountid>.r2.cloudflarestorage.com
S3_REGION=auto
S3_BUCKET=inbox-mail
S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
S3_FORCE_PATH_STYLE=false
# Optional parsed body-part blob uploads for Standard tenants
INBOX_STORE_PARSED_PARTS=false
OTEL_ENABLED=false
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/v1/traces
OTEL_SERVICE_NAME=inbox
OTEL_SAMPLE_RATIO=1.0
OTEL_CONSOLE_LOGS=true- Port
25(transfer): accepts incoming SMTP mail, verifies SPF/DKIM/DMARC, applies policy fromSMTP_REQUIRE_*, and stores accepted RFC822 messages in S3-compatible storage whenS3_*vars are set. - Object key layout:
- raw source (encrypted):
msg/{org}/{mailbox}/{message_id}.eml - attachments (encrypted):
att/{org}/{mailbox}/{message_id}/{part_id} - optional parsed body parts (encrypted):
part/{org}/{mailbox}/{message_id}/{mime_path}
- raw source (encrypted):
- Without
S3_*config, inbox stores the same key hierarchy under localINBOX_INCOMING_DIR(defaultdata/incoming). - Port
587(submission): accepts SMTP submissions and queues outbound jobs in DB (outbound_queue), then worker delivery claims jobs with DB leases (queued -> processing) for distributed-safe execution across instances. - Outbound hard limits (fixed, non-configurable): message size
25 MB(full RFC822 payload) and attachment payload18 MBper part. - Queue leasing controls:
SMTP_OUTBOUND_QUEUE_BATCH_SIZESMTP_OUTBOUND_QUEUE_LEASE_SECS
- Queue retries and expiry:
- exponential backoff (
SMTP_OUTBOUND_RETRY_*) - max attempts (
SMTP_OUTBOUND_MAX_ATTEMPTS) - TTL (
SMTP_OUTBOUND_TTL_SECS)
- exponential backoff (
- Queue cleanup controls:
SMTP_OUTBOUND_QUEUE_CLEANUP_INTERVAL_SECSSMTP_OUTBOUND_DEAD_JOB_RETENTION_SECSSMTP_OUTBOUND_DELIVERY_RETENTION_SECS
- SMTP rate limiting is configurable by env (
SMTP_RATE_LIMIT_*) and enforced per source IP for connection and message acceptance using DB-backed window buckets (distributed-safe across instances). - Multi-tenant: per-domain DKIM and auth policy are resolved from SurrealDB table
has_domain(IN organization,OUT domain) with optional override fields on the edge. - Tenant encryption options on
has_domain:enc_mode:standardore2eeenc_sse_c_key_b64: SSE-C key used for S3 PutObject instandardenc_wrapped_dek_customer: customer-wrapped key metadata fore2eeoutbound_dnssec_validate: optional per-domain override for DNSSEC-validated TLSA lookup (true/false)- quota controls (optional):
quota_max_recipients_per_message,quota_hourly_send_limit,quota_daily_send_limit,quota_max_queued_jobs
- Search indexing stays metadata-only in
e2eemode (no server-side parsed body blobs). - Full TLS posture defaults:
SMTP_OUTBOUND_TLS_MODE=requiredenforces TLS delivery only (no plaintext fallback)SMTP_OUTBOUND_DANE_MODE=preferdiscovers TLSA records and prioritizes DANE-protected MX hosts (requireenforces TLSA presence)SMTP_OUTBOUND_DNSSEC_VALIDATE=truerequires DNSSEC-validated TLSA lookup chain for DANE trust (can be overridden byhas_domain.outbound_dnssec_validate)- DANE hosts are cryptographically verified against TLSA records (usage/selector/matching) before SMTP DATA
SMTP_OUTBOUND_MTA_STS_ENFORCE=trueforces TLS when_mta-sts.<domain>TXT advertisesv=STSv1- TLS failures are appended as JSONL reports in
SMTP_TLS_REPORT_DIR
- OpenTelemetry tracing export can be enabled with
OTEL_ENABLED=trueand sent toOTEL_EXPORTER_OTLP_ENDPOINT. - Delivery failures that expire or exceed attempts are marked
deadinoutbound_queue; DSN.emlartifacts are written underdata/bounces/.- outbound delivery skips suppressed recipients via
suppressiontable (scope=address|domain) - queue worker applies suppression upserts using deterministic bounce classification (address/domain policy)
- queue worker emits structured tracing events for deliver/retry/dead-letter outcomes
- outbound delivery skips suppressed recipients via
send_email now performs real SMTP delivery:
mutation Send {
sendEmail(
from: "sender@example.com"
to: ["user@example.net"]
subject: "Hello"
textBody: "Hi from Inbox"
htmlBody: "<p>Hi from Inbox</p>"
idempotencyKey: "send-2026-02-10-001"
)
}