SMTP Configuration

1. Overview

Outbound email is sent by admin-service (registration confirmations, membership cards, password resets, receipts) and occasionally by portals (logout confirmations, system alerts). All services use Spring’s JavaMailSender via the stock spring-boot-starter-mail. Configuration is environment-specific — prod uses Microsoft 365 / Exchange Online via myriadevents-co-za.mail.protection.outlook.com; dev uses GreenMail.

This page documents the config surface, the production SMTP provider, and the dev fallback.

2. Configuration Surface

Every service exposes the same spring.mail. tree. Values flow from Helm → ConfigMap + Secret → env vars → application-.yml.

spring:
  mail:
    host: ${MAIL_HOST}
    port: ${MAIL_PORT:25}
    username: ${MAIL_USERNAME}
    password: ${MAIL_PASSWORD}
    protocol: smtp
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
          ssl:
            trust: ${MAIL_HOST}
    test-connection: false

Service-specific extras:

application:
  mail:
    from: ${MAIL_FROM:noreply@localhost}
    base-url: https://${MAIL_BASE_URL}     # used in email templates for absolute links

application.mail.from is the From: header on every outbound message; must be a deliverable address at the provider (Exchange rejects senders not owned by the tenant).

3. Production: Microsoft 365 / Exchange Online

Provider: myriadevents-co-za.mail.protection.outlook.com:25 with STARTTLS and basic auth.

From ArgoCD event-admin-service-prod.yml:

mail:
  host: myriadevents-co-za.mail.protection.outlook.com
  port: 25
  username: [email protected]
  # password: via existingsecret.mailpassword
  protocol: smtp
  tls: true
  properties.mail.smtp:
    auth: true
    ssl.trust: myriadevents-co-za.mail.protection.outlook.com
    starttls.enable: true

Notes:

  • Port 25 — standard SMTP; the Exchange connector accepts STARTTLS on 25. Port 587 (submission) also works if 25 is blocked by network egress policy.

  • From address[email protected]. Must match the authenticated identity or Exchange rejects with 5.7.1 Client does not have permissions to send as this sender.

  • SPF / DKIM — configured at the myriadevents.co.za DNS level. Any outbound email not coming through this connector will fail SPF at the recipient; keep that in mind if a future integration tries to send directly via a third-party mail provider.

  • Rate limits — Exchange Online has per-connector and per-sender throttles (typically hundreds per hour for regular accounts, thousands for connector-style). Large batch emails (membership card annual mail-out) must be throttled on the application side; the service’s CommunicationLog tracks sends and is the enforcement point.

3.1. Per-tenant From addresses

Multi-tenant portals may want to send as [email protected] for each tenant. Two approaches:

  1. Single shared connector, rewrite From at the application layer. Exchange permits you to send as [email protected] only if the myriadevents.co.za tenant has been delegated that domain (unlikely for most tenants).

  2. Per-tenant SMTP config. Each Organisation has its own mail.host/port/username/password/from. Service picks the right config per sending context. Scales better for genuine multi-tenancy but adds config complexity.

Currently all tenants share the myriadevents.co.za sender; scale to per-tenant only when a tenant specifically asks for branded From addresses. Capture the pattern in a design-journal thread when it becomes concrete.

4. Dev: GreenMail

Provider: local GreenMail container (image: greenmail/standalone:2.1.0).

# admin-service/src/main/resources/config/application-dev.yml (relevant part)
spring:
  mail:
    host: localhost
    port: 3025
    properties:
      mail:
        smtp:
          auth: false          # GreenMail accepts anything
          starttls:
            enable: false

See GreenMail Email Server for the full dev setup.

Mail browsing:

4.1. dev-to-stage SMTP for bigger testing

If dev needs realistic mail flow (e.g. testing that a customer actually receives a registration confirmation), switch application-dev.yml to the Exchange connector — but point application.mail.from at a test address and be careful about volume. Typically this is only needed briefly; revert after.

5. Stage

Stage uses the same Exchange connector as prod. Reason: we explicitly want stage to send real mail during UAT, to a small known list of test addresses ([email protected], etc.). Do not point stage at dev GreenMail — it silences the very thing we’re testing.

ArgoCD event-admin-service-stage.yml mirrors prod mail config; the only deltas are database URL and image version.

6. Template Rendering

Templates live in admin-service as Thymeleaf files under src/main/resources/templates/mails/. Variables resolved against the service’s MailService; email is assembled and sent synchronously.

Advanced: the "email implementation layer" design (see design-journal/2026-03/email-implementation.adoc) introduces DB-backed org-scoped templates via MJML + Thymeleaf. When that lands, the mail provider configuration stays identical; only the template-rendering layer changes.

7. Observability

Sent email is logged at INFO:

INFO  ... MailService : Email sent to '[email protected]' with subject 'Registration confirmation' in 187ms

Failures log at ERROR with the full exception chain. For production debugging, CommunicationLog records every attempt with delivery status.

No email body or PII is logged. Subject is considered low-sensitivity and is logged.

8. Security

  • Credentials — always via Kubernetes Secret. Never in values.yaml, never in git, never in application-prod.yml.

  • TLS — mandatory via starttls.enable: true for any non-dev environment. Exchange rejects plaintext after the TLS handshake requirement is active.

  • Rate-limit — application-level throttling for bulk sends; Exchange enforces its own throttle and will 4.7.0 responses if exceeded. Handle the retryable SMTP error in MailService.

9. New Service Onboarding

When admin-portal eventually needs to send email (unlikely at launch — admin-portal typically triggers emails via admin-service, not directly):

  1. Copy spring.mail.* tree from registration-portal’s application-prod.yml.

  2. Add mail section to values.yaml with the same structure.

  3. Point at event-admin-portal Secret for mailpassword.

  4. No new Exchange connector config needed — reuse the tenant-level connector.

Preferred: do not add mail to admin-portal. Route all outbound email through admin-service where CommunicationLog captures the send. Portals should request emails (via admin-service API) rather than send directly.

10. Reference

File Role

admin-service/src/main/resources/config/application.yml

Base spring.mail.* structure

admin-service/src/main/resources/config/application-dev.yml

Dev override pointing at GreenMail

admin-service/src/main/resources/templates/mails/

Thymeleaf email templates

~/dev/idl-xnl-jhb-rc01/argocd/event-admin-service-prod.yml

Production Exchange connector config

admin-service/src/main/java/…​/service/mail/MailService.java

Service-level sender

12. Change History

Date Change

2026-04-24

Initial draft. Grounded in ArgoCD prod manifests.