README.md
June 24, 2026 · View on GitHub
███████╗████████╗██████╗ ██╗ ██╗██╗ ██╗███████╗
██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝██║ ██╔╝██╔════╝
███████╗ ██║ ██████╔╝ ╚████╔╝ █████╔╝ █████╗
╚════██║ ██║ ██╔══██╗ ╚██╔╝ ██╔═██╗ ██╔══╝
███████║ ██║ ██║ ██║ ██║ ██║ ██╗███████╗
╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝
[ e m a i l ]
[EMAIL + CAMPAIGN CLIENT FOR STRYKE // SMTP SEND + MASS MAILING + TEMPLATES + UNSUBSCRIBE + SUPPRESSION]
"Send the newsletter, not the spam — through your own SMTP, with the unsubscribe baked in."
Transactional and campaign email for stryke. Single send, personalized mass
mailing, {{merge}} templates, List-Unsubscribe headers, suppression lists,
and rate limiting — all over your own authenticated SMTP (lettre, rustls,
no tokio). Opt-in package tier.
strykelang · MenkeTechnologiesMeta · stryke-mongo · stryke-redis
Read the Docs · Engineering Report
Table of Contents
- [0x00] Install
- [0x01] Send one message
- [0x02] Mass mailing
- [0x03] Connecting
- [0x04] Compliance
- [0x05] API reference
- [0x06] Build & test
- [0x07] License
[0x00] Install
s add github.com/MenkeTechnologies/stryke-email
On first use Email, stryke dlopens the cdylib in-process and registers every
email__* export. A pooled SmtpTransport is cached per (host, port, tls, user) for the life of the process.
[0x01] Send one message
use Email
var %conn = ( host => "smtp.example.com", username => "me", password => $ENV{SMTP_PASS} )
Email::send(
{
from => "Me <me@example.com>",
to => "customer@example.com",
subject => "Your license key",
text => "Thanks for your purchase. Key: ABC-123",
html => "<p>Thanks for your purchase. Key: <code>ABC-123</code></p>",
},
%conn,
)
[0x02] Mass mailing
Personalized, suppression-aware, rate-limited, with the unsubscribe header injected automatically:
val $res = Email::send_bulk(
{ subject => "Hi {{name}}, {{product}} v2 is out",
html => "<p>Hi {{name}}, your {{product}} update is ready.</p>" },
[
{ email => "ada@example.com", name => "Ada", vars => { product => "Audio-Haxor" } },
{ email => "alan@example.com", name => "Alan", vars => { product => "traderview" } },
],
from => "Me <me@example.com>",
suppression => Email::suppress_filter([], $unsub_list)->{removed}, # your opt-outs
rate => { per_minute => 120 },
list_unsubscribe => { url => "https://example.com/u?e={{email}}", mailto => "unsub@example.com" },
%conn,
)
p $res->{sent} # 2
p $res->{failed} # 0
p $res->{results} # [ { email, ok, error? }, ... ]
Each recipient's template is merged with their vars (plus email/name), the
list_unsubscribe URL can itself use {{email}} for a per-recipient link, and
sends are throttled by rate.
[0x03] Connecting
%conn (or $SMTP_HOST / $SMTP_USER / $SMTP_PASS as a fallback):
| Key | Default | Notes |
|---|---|---|
host | — (required) | SMTP server hostname |
tls | starttls | starttls (587), tls (465), or none (25) |
port | by tls | Override the TLS-implied default |
username | — | SMTP auth user |
password | — | SMTP auth password |
[0x04] Compliance
This package is for sending to your own opted-in recipients through your own SMTP — newsletters, transactional mail, product announcements. It ships the mechanisms that keep that legitimate:
- List-Unsubscribe (RFC 2369 / 8058 one-click) auto-added to every
send_bulkmessage when you passlist_unsubscribe. - Suppression lists —
Email::suppress_filterandsend_bulk'ssuppressionhonor opt-outs and bounces. - Rate limiting —
rate => { per_minute }/{ delay_ms }for deliverability and to stay within your provider's limits. - Address validation before sending.
Obtaining consent and honoring unsubscribes (CAN-SPAM, GDPR, your provider's ToS) is the sender's responsibility. The package gives you the tools, not a way around them.
[0x05] API reference
| Group | Functions |
|---|---|
| Send | send, send_raw, send_bulk, send_template, build_message, verify_connection |
| Templates | render, merge, merge_many |
| Addresses | validate, validate_list, validate_message, parse, split_addresses, format_address, domain, normalize |
| Lists | unsubscribe_header, suppress_filter, dedupe, chunk, group_by_domain, mailto |
| URL | parse_url, build_url, redact_url |
| Meta | version |
The template, address, and compliance helpers take no connection and are unit-tested in-crate, so they validate in CI with no SMTP server.
[0x06] Build & test
make debug # cargo build
make test # cargo test, then `s test t/` (live sends need $SMTP_HOST)
make install # s pkg install -g .
cargo test runs the in-crate unit tests (merge, validation, parsing,
unsubscribe value, multipart build) with no server. Point $SMTP_HOST at a
local catch-all like MailHog (tls => none, port 1025) to exercise the send
path without emailing anyone real.
[0x07] License
MIT · MenkeTechnologies