System Architecture

System Architecture

Overview

tela's email system is a transactional email subsystem designed for reliability and flexibility, supporting both self-hosted and managed deployments. It provides a consistent, branded email experience across user actions like verification, password reset, and organization invites, while maintaining compatibility with email clients that force dark mode rendering.

Components

Mailer Interface

The mailer package defines a Mailer interface for sending transactional emails, with implementations for SMTP and logging. FromEnv() selects the appropriate driver based on environment variables, defaulting to a LogMailer if no SMTP host is configured. LogMailer prints emails to the server log, enabling development and first-boot flows without an email relay.

Sources: backend/internal/mailer/mailer.go:1-63

SMTP Driver

The smtpMailer implementation uses the go-mail library to send emails via an SMTP relay. It supports TLS options (starttls, ssl, none), authentication, and connection pooling via per-send dialing. It is provider-agnostic, allowing use with services like Resend, Postmark, SES, or self-hosted Postfix.

Sources: backend/internal/mailer/smtp.go:1-103

Email Templates

Emails are rendered using a Go template engine, with a consistent light-themed design optimized for email client compatibility. The emailView struct holds all data required for rendering, including branding, content, and CTAs. Templates are styled with inline CSS, using a fixed color palette for consistent rendering.

Sources: backend/internal/mailer/templates.go:11-20, backend/internal/mailer/templates.go:21-36, backend/internal/mailer/templates.go:98-124, backend/internal/mailer/templates.go:197-287

Branding and Customization

Email branding is handled via the Brand struct, which allows customization of the product name, logo, and accent color. A zero Brand results in the default "tela" branding. The applyBrand method white-labels the email view, applying the brand only when an organization name is provided.

Sources: backend/internal/mailer/templates.go:83-88, backend/internal/mailer/templates.go:165-168, backend/internal/mailer/templates.go:181-192

Email Content Types

Emails are structured into transactional and notification types. Transactional emails (e.g., password reset, verification) are sent for user actions, while notification emails are sent for user interactions (e.g., mentions). Templates support optional fields like snippets, diffs, and related links.

Sources: backend/internal/mailer/templates.go:59-63, backend/internal/mailer/templates.go:382-396, backend/internal/mailer/templates.go:400-414, backend/internal/mailer/templates.go:419-438, backend/internal/mailer/templates.go:445-473

Control and Data Flow

  1. A user action triggers an email send (e.g., VerifyEmail, ResetPassword).
  2. A Message is constructed with HTML and Text content, and optionally marked Important.
  3. The Mailer interface is used to send the message.
  4. If SMTP is configured, smtpMailer.Send() is called, creating a new client and sending via SMTP.
  5. If SMTP is not configured, LogMailer.Send() logs the email instead.
  6. Email content is rendered using renderHTML and renderText, applying branding and templates.
graph TD
    A["User Action"] --> B["Mailer.Send()"]
    B --> C["LogMailer.Send()"]
    B --> D["smtpMailer.Send()"]
    C --> E["Log Email"]
    D --> F["Create SMTP Client"]
    F --> G["Send Email via SMTP"]
    G --> H["Email Delivered"]
    H --> I["User Receives Email"]
    B --> J["Render HTML/Text"]
    J --> K["Apply Branding"]
    K --> L["Finalize Template"]
    L --> M["HTML Body"]
    M --> N["Text Body"]
    N --> O["Send Message"]

Why This Design?

The design prioritizes reliability and configurability. By supporting both SMTP and logging, tela can be used in environments without an email relay, such as local development or first-time self-hosting. The use of inline CSS ensures consistent rendering across email clients, which often force dark mode rendering that would break a dark-themed email.

Sources: backend/internal/mailer/mailer.go:1-63, backend/internal/mailer/templates.go:11-20, backend/internal/mailer/templates.go:83-88, backend/internal/mailer/templates.go:165-168, backend/internal/mailer/templates.go:181-192