Mail — Usage & Configuration ✅
Overview 💡
This document describes the ZinTrust Mail API, supported drivers, attachment handling (via Storage disks), and test helpers. All runtime mail configuration is config-first and accessed via Env & src/config/mail.ts. Drivers are pluggable and follow the repository's no-classes rule (sealed namespaces / plain functions).
Interface Reference
export type SendMailInput = {
to: string | string[];
subject: string;
text: string;
html?: string;
from?: {
address?: string;
name?: string;
};
attachments?: AttachmentInput[];
};
export type SendMailResult = {
ok: boolean;
driver: 'sendgrid' | 'disabled' | 'smtp' | 'ses' | 'mailgun' | 'nodemailer';
messageId?: string;
};Quick start ✉️
Send a basic email:
import { Mail } from '@zintrust/core';
await Mail.send({
to: 'user@example.com',
subject: 'Hello',
text: 'Welcome!',
});Attachments (disk + inline) 📎
You can attach files by passing either inline content or a { disk, path } reference.
Inline attachment:
await Mail.send({
to: 'a@b.com',
subject: 'Report',
text: 'See attached',
attachments: [{ content: Buffer.from('data'), filename: 'file.txt' }],
});Disk-based attachment (reads from configured Storage disk):
await Mail.send({
to: 'a@b.com',
subject: 'Invoice',
text: 'Attached',
attachments: [{ disk: 'local', path: 'invoices/1.pdf', filename: 'invoice.pdf' }],
});Notes:
Mail.send()resolves disk attachments using the Storage registry (Storage.getDisk()), so drivers must implementget()andexists()(as our drivers do).- If a disk attachment cannot be found a
NotFoundErroris thrown.
Drivers & behavior 🔧
- disabled — no sends
- sendgrid — HTTP API (encodes attachments base64)
- mailgun — HTTP API (multipart form upload)
- smtp — Node.js SMTP implementation (supports STARTTLS / SMTPS, and attachments via multipart/mixed)
- ses — AWS SES (SigV4 signed)
Driver selection is via MAIL_DRIVER (see env vars below).
Install drivers
zin add mail:smtp
zin add mail:sendgrid
zin add mail:mailgun
zin add mail:nodemailerTemplates (code + Markdown) 🧩
ZinTrust includes two lightweight templating options:
- Code templates (plain strings)
import { MailTemplates, MailTemplateRenderer } from '@zintrust/core';
const tpl = MailTemplates.auth.welcome;
const rendered = MailTemplateRenderer.render(tpl, { name: 'Jane' });- Markdown templates (stored as
.mdfiles)
Templates live in src/tools/mail/templates/markdown/ and can be listed/rendered:
import { listTemplates, renderTemplate } from '@zintrust/core/node';
const names = listTemplates();
const { html, meta } = renderTemplate('auth/welcome', { name: 'Jane' });Markdown templates can include top-of-file metadata:
<!-- Subject: Welcome, {{name}}! -->
<!-- Preheader: Getting started -->
<!-- Variables: name -->You can pass the resulting html into Mail.send({ html, ... }), and use meta.subject as your message subject.
Testing (fakes) 🧪
Use the MailFake to assert sends without performing I/O:
import { MailFake } from '@zintrust/core/node';
MailFake.reset();
await MailFake.send({ to: 'u@x.com', subject: 'x', text: 't' });
MailFake.assertSent((r) => r.to.includes('u@x.com'));For integration tests that involve attachments, combine FakeStorage + MailFake to verify content flows from storage into the outgoing message.
Environment variables (important) ⚙️
- MAIL_DRIVER (disabled | sendgrid | mailgun | smtp | ses) — default:
disabled - MAIL_FROM_ADDRESS — default: `` (required for sends)
- MAIL_FROM_NAME — display name
- SENDGRID_API_KEY — required when
MAIL_DRIVER=sendgrid - MAILGUN_API_KEY — required when
MAIL_DRIVER=mailgun - MAILGUN_DOMAIN — required when
MAIL_DRIVER=mailgun - MAILGUN_BASE_URL — optional (default:
https://api.mailgun.net) - MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD, MAIL_SECURE — SMTP credentials
- AWS_REGION — used by SES driver
Tip: Keep secrets CLI-managed with
src/Toolkit/Secretsand surfaced to.env.pull.