Mailer And SMTP
Geblang includes two layers for email:
mailer: source-level classes for application code.smtp: low-level native functions for MIME rendering and SMTP delivery.
Most applications should import mailer. It gives you typed message,
attachment, transport, and sender objects while still producing plain MIME and
using SMTP underneath.
Quick Start
import mailer;
let message = mailer.Message("Welcome")
.fromAddress("App <noreply@example.com>")
.toAddress("Ada <ada@example.com>")
.withText("Hello Ada,\n\nYour account is ready.")
.withHtml("<h1>Hello Ada</h1><p>Your account is ready.</p>");
let transport = mailer.smtpTransport("smtp.example.com", 587, "user", "secret")
.fromAddress("App <noreply@example.com>");
let client = mailer.Mailer(transport);
client.send(message);
fromAddress may be set on the message or the transport. The message-level
sender wins; the transport sender is useful when most mail from an application
uses the same address.
Messages
mailer.Message(subject) starts an email message. It supports fluent mutation:
let message = mailer.Message("Invoice")
.fromAddress("Billing <billing@example.com>")
.toAddress("customer@example.com")
.ccAddress("accounts@example.com")
.bccAddress("audit@example.com")
.withReplyTo("support@example.com")
.withHeader("X-Customer-Id", "cust_123")
.withText("Your invoice is attached.")
.withHtml("<p>Your invoice is attached.</p>");
Methods:
| Method | Description |
|---|---|
fromAddress(address) |
Sets the sender. |
toAddress(address) |
Adds a To recipient. |
ccAddress(address) |
Adds a Cc recipient. |
bccAddress(address) |
Adds a blind-copy recipient used for SMTP delivery but omitted from rendered headers. |
withReplyTo(address) |
Sets Reply-To. |
withText(text) |
Sets the plain text body. |
withHtml(html) |
Sets the HTML body. |
withHeader(name, value) |
Adds a custom header. Reserved MIME and address headers are controlled by the message object. |
attach(attachment) |
Adds an attachment. |
render() |
Returns the MIME message as a string without sending. |
toDict() |
Returns the low-level dictionary shape consumed by smtp. |
When both text and HTML are present, Geblang renders a
multipart/alternative body. When attachments are present, the message becomes
multipart/mixed with the text/HTML body as the first part.
Attachments
Use mailer.Attachment(filename, content) for in-memory content. content may
be a string or bytes.
import bytes;
import mailer;
let report = mailer.Attachment("report.txt", "daily summary")
.withContentType("text/plain");
let image = mailer.Attachment("logo.png", bytes.fromBase64("..."))
.withContentType("image/png")
.asInline("logo");
Use attachmentFromPath(path, contentType) when the content should be read from
disk as the message is rendered or sent:
let pdf = mailer.attachmentFromPath("build/invoice.pdf", "application/pdf");
message.attach(pdf);
Inline attachments set Content-Disposition: inline and a Content-ID header.
Reference them from HTML with cid:<id>:
let message = mailer.Message("Logo")
.withHtml("<img src=\"cid:logo\">")
.attach(mailer.Attachment("logo.png", imageBytes).withContentType("image/png").asInline("logo"));
SMTP Transport
mailer.SmtpTransport(host, port, username, password) configures SMTP.
let transport = mailer.SmtpTransport("smtp.example.com", 587, "user", "secret")
.fromAddress("App <noreply@example.com>")
.withStartTLS(true)
.withTLS(false)
.withHello("app.example.com");
Transport methods:
| Method | Description |
|---|---|
fromAddress(address) |
Default sender when the message has no sender. |
withStartTLS(enabled) |
Enables STARTTLS upgrade when the server advertises it. Enabled by default. |
withTLS(enabled) |
Uses implicit TLS for providers that expect TLS from connection start, commonly port 465. |
withHello(name) |
Sends a custom SMTP HELO/EHLO hostname. |
allowInsecureTLS(enabled) |
Disables TLS certificate verification. Use only for local development or controlled test servers. |
toDict() |
Returns the low-level dictionary shape consumed by smtp.send. |
Low-Level smtp
The native smtp module accepts dictionaries. This is useful for framework
code, tests, or adapter modules.
import smtp;
let message = {
"from": "App <noreply@example.com>",
"to": ["Ada <ada@example.com>"],
"subject": "Welcome",
"text": "Hello Ada",
"html": "<p>Hello Ada</p>",
"attachments": [
{
"filename": "welcome.txt",
"contentType": "text/plain",
"content": "Thanks for trying Geblang."
}
]
};
let raw = smtp.message(message);
smtp.send(config, message) sends through an SMTP server and returns a dict:
let result = smtp.send({
"host": "smtp.example.com",
"port": 587,
"username": "user",
"password": "secret",
"from": "App <noreply@example.com>",
"startTLS": true
}, message);
io.println(result["ok"]);
io.println(result["recipients"]);
Message dictionary keys:
| Key | Type | Description |
|---|---|---|
from |
string |
Sender address. Optional if config has from. |
to |
string or list<string> |
Primary recipients. |
cc |
string or list<string> |
Carbon-copy recipients. |
bcc |
string or list<string> |
Blind-copy recipients. Not rendered as a header. |
replyTo |
string |
Reply-To address. |
subject |
string |
Subject line. |
text |
string |
Plain text body. |
html |
string |
HTML body. |
headers |
`dict<string, string | list |
attachments |
list<dict> |
Attachment dictionaries. |
Attachment dictionary keys:
| Key | Type | Description |
|---|---|---|
filename |
string |
Filename shown to recipients. Required for direct content. |
content |
string or bytes |
In-memory attachment content. |
path |
string |
File path to read. If present, filename defaults to the file base name. |
contentType |
string |
MIME type. Defaults to application/octet-stream. |
inline |
bool |
Uses inline disposition instead of attachment. |
contentId |
string |
Content ID for inline attachments. |
Config dictionary keys:
| Key | Type | Description |
|---|---|---|
host |
string |
SMTP server hostname. Required. |
port |
int |
SMTP server port. Defaults to 587. |
username |
string |
SMTP username. Optional. |
password |
string |
SMTP password. Optional. |
from |
string |
Default sender. |
startTLS |
bool |
Upgrade with STARTTLS when available. Defaults to true. |
tls |
bool |
Use implicit TLS from connection start. Defaults to false. |
hello |
string |
Custom HELO/EHLO hostname. |
insecureSkipVerify |
bool |
Skip certificate verification. Defaults to false. |
Testing Mail
For tests and previews, render instead of sending:
let raw = client.render(message);
assert(raw.contains("multipart/alternative"));
assert(raw.contains("Subject: Welcome"));
This avoids network access and makes assertions deterministic. A later
application framework can wrap mailer.Mailer behind an interface and provide
an in-memory test transport.