MailForge is a layered framework:
Developer App → Communication → Layer 1: Framework → Layer 2: Provider SDK · MailForge Studio → Internet
Communication layer. MailForge.Communication adds a channel-agnostic
INotificationSender on top of Layer 1. The EmailChannel adapter delegates to
IEmailSender, so email behavior is unchanged; MailForge.Telegram delivers text, media,
and keyboards through the Telegram Bot API — the first non-email channel, with more to
follow. See Communication.
Layer 1 — Framework. The core pipeline: message models and builders, typed emails,
template rendering, validation, retries, auditing, middleware, and failover. You interact
with a single IEmailSender.
Layer 2 — Provider SDK. One adapter per email provider, exposed as an
IEmailProvider. Each provider translates an EmailMessage into the provider’s wire
format and reports a ProviderDeliveryResult (success, provider message id, details).
MailForge Studio. A local development inbox that captures messages through an
IEmailProvider of its own, stores them in SQLite, and serves a web dashboard plus an
SMTP relay. It never talks to the network.
┌───────────────────────────────────────────────────────────────────────────┐
│ Developer App │
│ │ │
│ ▼ │
│ COMMUNICATION INotificationSender (email + Telegram channels) │
│ │ routes Notification → channel → middleware │
│ ▼ │
│ LAYER 1 IEmailSender │
│ │ template render → validation → retry → logging → audit → your mw │
│ ▼ │
│ LAYER 2 IEmailProvider (SMTP · Resend · SES · Postmark · Mailgun · │
│ Brevo · ZeptoMail · AzureCS) · MailForge Studio (local) │
│ │ │
│ └──────────────► Internet ───────────────────────────► │
└───────────────────────────────────────────────────────────────────────────┘
When you call IEmailSender.SendAsync, EmailSender runs the message through a fixed
pipeline before handing it to the active provider:
Render body (template or inline content)
→ validate (DefaultEmailValidator + your validators)
→ RetryEmailMiddleware
→ LoggingEmailMiddleware
→ AuditEmailMiddleware (when configured)
→ your middleware (AddMiddleware, in registration order)
→ provider.SendAsync
Email<TModel> bodies through the template renderers.EmailException.IsTransient == true) up to the
configured attempt count (WithRetries, default 3) with an exponential base delay.ILogger is available.IEmailAuditSink.RecordAsync when
UseAuditSink is configured.UseFailover wraps multiple providers. A message is attempted on each ProviderFailoverRoute
in precedence order until one succeeds or the routes are exhausted. Routes carry a
FailoverPolicy (TransientOnly or AnyFailure) and a per-route MaxAttempts.
Current semantics: routes are evaluated per attempt in precedence order; there is no promotion back to an earlier provider after it recovers. See Failover for the exact rules and worked examples.
Providers advertise what they support through ProviderCapabilities — attachments, inline
images, custom headers, tags, and whether both bodies are required. The framework reads
these to guide what you can safely send through a given provider. See
Provider Capabilities.
The public API is stable:
AddMailForge(Action<MailForgeBuilder>) registers:
IEmailSender (singleton) with the configured pipelineIEmailProvider (singleton; FakeEmailProvider when none is configured)ITemplateRegistry, IInlineTemplateRenderer, IEmailTemplateRendererThe builder methods: UseProvider, UseDefaultFrom, EnableAutoPlainText, WithRetries,
UseAuditSink, RegisterTemplate, AddValidator, AddMiddleware, UseFailover.
AddCommunication(Action<CommunicationBuilder>) (from MailForge.Communication) registers
INotificationSender, IChannelRegistry, and your channels on top of the existing
registration. The CommunicationBuilder methods: UseChannel, UseChannel(factory),
UseEmailChannel, AddMiddleware. Email stays the single shipping channel — see
Communication.