Zetian
A professional, high-performance SMTP server library for .NET with minimal dependencies. Features TLS/SSL, authentication, rate limiting, and extensible architecture for building custom email servers.
Install / Use
/learn @Taiizor/ZetianREADME

Zetian SMTP Server
A professional, high-performance SMTP server library for .NET with minimal dependencies. Build custom SMTP servers with ease using a fluent API and extensible architecture.
Features
- 🔒 Security: Full TLS/SSL support with STARTTLS
- 📦 Minimal Dependencies: Only essential packages required
- 🎯 Multi-Framework: Supports .NET 6.0, 7.0, 8.0, 9.0, and 10.0
- 🛡️ Rate Limiting: Protect against abuse with configurable rate limits
- 🔑 Authentication: Built-in PLAIN and LOGIN mechanisms, easily extensible
- 📊 Event-Driven: Rich event system for message processing and monitoring
- 🚀 High Performance: Efficient async/await patterns and optimized I/O operations
- 🔧 Extensible: Plugin architecture for custom authentication, filtering, and processing
Installation
dotnet add package Zetian
Or via NuGet Package Manager:
Install-Package Zetian
Quick Start
Basic SMTP Server
using Zetian.Server;
// Create and start a basic SMTP server
using var server = SmtpServerBuilder.CreateBasic();
server.MessageReceived += (sender, e) =>
{
Console.WriteLine($"Received message from {e.Message.From}");
Console.WriteLine($"Subject: {e.Message.Subject}");
};
await server.StartAsync();
Console.WriteLine($"Server running on {server.Endpoint}");
// Keep running...
Console.ReadKey();
await server.StopAsync();
Authenticated SMTP Server
using Zetian.Server;
using var server = new SmtpServerBuilder()
.Port(587)
.RequireAuthentication()
.AllowPlainTextAuthentication()
.SimpleAuthentication("user", "password")
.Build();
await server.StartAsync();
Secure SMTP Server with TLS
using Zetian.Server;
using var server = new SmtpServerBuilder()
.Port(587)
.Certificate("certificate.pfx", "password")
.RequireSecureConnection()
.Build();
await server.StartAsync();
Advanced Configuration
Using the Fluent Builder
using System.Net;
using Zetian.Models;
using Zetian.Server;
var server = new SmtpServerBuilder()
.Port(587)
.BindTo(IPAddress.Any)
.ServerName("My SMTP Server")
.MaxMessageSizeMB(25)
.MaxRecipients(100)
.MaxConnections(50)
.MaxConnectionsPerIP(5)
// Security
.Certificate(certificate)
.RequireAuthentication()
.RequireSecureConnection()
// Authentication
.AddAuthenticationMechanism("PLAIN")
.AddAuthenticationMechanism("LOGIN")
.AuthenticationHandler(async (username, password) =>
{
// Your authentication logic
if (await ValidateUser(username, password))
return AuthenticationResult.Succeed(username);
return AuthenticationResult.Fail();
})
// Features
.EnablePipelining()
.Enable8BitMime()
// Timeouts
.ConnectionTimeout(TimeSpan.FromMinutes(5))
.CommandTimeout(TimeSpan.FromSeconds(30))
.DataTimeout(TimeSpan.FromMinutes(2))
// Retry Configuration
.MaxRetryCount(3)
// Logging
.LoggerFactory(loggerFactory)
.EnableVerboseLogging()
.Build();
Extensions
Rate Limiting
using Zetian.Models;
using Zetian.Extensions;
server.AddRateLimiting(
RateLimitConfiguration.PerHour(100)
);
Message Filtering
🔍 Important: Two Filtering Approaches
Zetian provides two different filtering approaches:
-
Protocol-Level Filtering (via Builder) - Rejects at SMTP command level
- Applied during MAIL FROM/RCPT TO commands
- More efficient, saves bandwidth
- Use
WithSenderDomainWhitelist,WithRecipientDomainWhitelistetc.
-
Event-Based Filtering (via Extensions) - Filters after message received
- Applied after the entire message is received
- More flexible for complex logic
- Use
AddSpamFilter,AddSizeFilter,AddMessageFilteretc.
Choose based on your needs:
- Use Protocol-Level for early rejection and better performance
- Use Event-Based for complex filtering logic or when you need the full message
Protocol-Level Filtering (Early Rejection)
// Configure filtering at build time - rejects at SMTP protocol level
var server = new SmtpServerBuilder()
.Port(25)
.WithSenderDomainWhitelist("trusted.com", "example.com") // Rejects at MAIL FROM
.WithSenderDomainBlacklist("spam.com", "junk.org") // Rejects at MAIL FROM
.WithRecipientDomainWhitelist("mydomain.com") // Rejects at RCPT TO
.MaxMessageSize(10 * 1024 * 1024) // Rejects at MAIL FROM
.WithFileMessageStore(@"C:\mail") // Stores at protocol level
.Build();
Event-Based Filtering (Late Rejection)
// Configure filtering via extensions - processes after message is received
// Add spam filter
server.AddSpamFilter(new[] { "spam.com", "junk.org" });
// Add size filter (10MB max)
server.AddSizeFilter(10 * 1024 * 1024);
// Add custom filter
server.AddMessageFilter(message =>
{
// Your filtering logic
return !message.Subject?.Contains("SPAM") ?? true;
});
Message Storage
// Event-based approach - saves after message is received
server.SaveMessagesToDirectory(@"C:\smtp_messages");
// Protocol-level approach - integrated storage during SMTP transaction
var server = new SmtpServerBuilder()
.Port(25)
.WithFileMessageStore(@"C:\smtp_messages") // Automatic storage
.Build();
// Custom message processing (event-based)
server.MessageReceived += async (sender, e) =>
{
// Save to database
await SaveToDatabase(e.Message);
// Forward to another service
await ForwardMessage(e.Message);
// Send notification
await NotifyAdministrator(e.Message);
};
Domain Validation
// Event-based approach - validates after message is received
server.AddAllowedDomains("example.com", "mycompany.com");
// Custom recipient validation
server.AddRecipientValidation(recipient =>
{
return IsValidRecipient(recipient.Address);
});
// Protocol-level approach - validates at SMTP command level
var server = new SmtpServerBuilder()
.Port(25)
.WithRecipientDomainWhitelist("example.com", "mycompany.com")
.Build();
Event Handling
// Session events
server.SessionCreated += (s, e) =>
Console.WriteLine($"New session from {e.Session.RemoteEndPoint}");
server.SessionCompleted += (s, e) =>
Console.WriteLine($"Session completed: {e.Session.Id}");
// Message events
server.MessageReceived += (s, e) =>
{
Console.WriteLine($"Message: {e.Message.Subject}");
// Reject message if needed
if (IsSpam(e.Message))
{
e.Cancel = true;
e.Response = new SmtpResponse(550, "Message rejected as spam");
}
};
// Error events
server.ErrorOccurred += (s, e) =>
Console.WriteLine($"Error: {e.Exception.Message}");
Custom Authentication
// Option 1: Create a custom authenticator class
using Zetian.Models;
using Zetian.Abstractions;
public class CustomAuthenticator : IAuthenticator
{
public string Mechanism => "CUSTOM";
public async Task<AuthenticationResult> AuthenticateAsync(
ISmtpSession session,
string? initialResponse,
StreamReader reader,
StreamWriter writer,
CancellationToken cancellationToken)
{
// Implement your custom authentication logic here
// ...
}
}
// Register custom authenticator
AuthenticatorFactory.Register("CUSTOM", () =>
new CustomAuthenticator());
// Option 2: Use the authentication handler with builder
using Zetian.Models;
using Zetian.Server;
var server = new SmtpServerBuilder()
.Port(587)
.RequireAuthentication()
.AuthenticationHandler(async (username, password) =>
{
// Check against database
var user = await db.GetUser(username);
if (user != null && VerifyPassword(password, user.PasswordHash))
{
return AuthenticationResult.Succeed(username);
}
return AuthenticationResult.Fail();
})
.AddAuthenticationMechanism("PLAIN")
.AddAuthenticationMechanism("LOGIN")
.Build();
Message Processing
server.MessageReceived += async (sender, e) =>
{
var message = e.Message;
// Access message properties
Console.WriteLine($"ID: {message.Id}");
Console.WriteLine($"From: {message.From?.Address}");
Console.WriteLine($"To: {string.Join(", ", message.Recipients)}");
Console.WriteLine($"Subject: {message.Subject}");
Console.WriteLine($"Size: {message.Size} bytes");
Console.WriteLine($"Has Attachments: {message.HasAttachments}");
Cons
Related Skills
node-connect
339.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.8kCommit, push, and open a PR
