README.md

June 24, 2026 · View on GitHub

 ███████╗████████╗██████╗ ██╗   ██╗██╗  ██╗███████╗
 ██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝██║ ██╔╝██╔════╝
 ███████╗   ██║   ██████╔╝ ╚████╔╝ █████╔╝ █████╗
 ╚════██║   ██║   ██╔══██╗  ╚██╔╝  ██╔═██╗ ██╔══╝
 ███████║   ██║   ██║  ██║   ██║   ██║  ██╗███████╗
 ╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝   ╚═╝  ╚═╝╚══════╝
                   [ e m a i l ]

CI License: MIT stryke

[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

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):

KeyDefaultNotes
host— (required)SMTP server hostname
tlsstarttlsstarttls (587), tls (465), or none (25)
portby tlsOverride the TLS-implied default
usernameSMTP auth user
passwordSMTP 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_bulk message when you pass list_unsubscribe.
  • Suppression listsEmail::suppress_filter and send_bulk's suppression honor opt-outs and bounces.
  • Rate limitingrate => { 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

GroupFunctions
Sendsend, send_raw, send_bulk, send_template, build_message, verify_connection
Templatesrender, merge, merge_many
Addressesvalidate, validate_list, validate_message, parse, split_addresses, format_address, domain, normalize
Listsunsubscribe_header, suppress_filter, dedupe, chunk, group_by_domain, mailto
URLparse_url, build_url, redact_url
Metaversion

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