An SMTP client and server library for Zig implementing RFC 5321.
0

Configure Feed

Select the types of activity you want to include in your feed.

1<!-- 2SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us> 3SPDX-License-Identifier: MIT 4--> 5 6# zsmtp 7 8An SMTP client and server library for Zig (RFC 5321). 9 10Both the client and the server run over plain `std.Io.Reader`/`std.Io.Writer` 11pairs, so they are transport-agnostic: wrap a TCP stream for real use, or 12fixed in-memory buffers in tests. Requires Zig 0.16. 13 14## Client 15 16```zig 17const zsmtp = @import("zsmtp"); 18 19var reply_buf: [1024]u8 = undefined; 20var client: zsmtp.Client = .init(&stream_reader.interface, &stream_writer.interface, &reply_buf); 21 22_ = try client.greet(); // read the 220 greeting 23_ = try client.hello("my-host.example.com"); // EHLO (HELO fallback), returns extensions 24try client.sendMail("me@example.com", &.{"you@example.net"}, message); 25try client.quit(); 26``` 27 28Line endings in the message are normalized to CRLF and leading dots are 29stuffed automatically. On `error.UnexpectedReply`, `client.last_reply` holds 30the server's actual code and text. `mailFrom`/`rcptTo`/`sendMessage` are also 31available individually, as is `authPlain`. 32 33## Server 34 35```zig 36var session: zsmtp.Server = .init(&stream_reader.interface, &stream_writer.interface, .{ 37 .context = &my_state, 38 .vtable = &.{ 39 .rcptTo = onRcptTo, // optional; accept/reject each recipient 40 .message = onMessage, // required; receives envelope + message data 41 }, 42}, .{ .hostname = "mx.example.com" }); 43try session.run(gpa); 44``` 45 46`run` serves one connection until QUIT or disconnect, enforcing command 47sequencing, recipient and message-size limits, and un-stuffing message data. 48Listening, accepting, and concurrency are up to the caller. 49 50## Demo CLI 51 52```sh 53zig build 54 55# Debug server that prints received messages to stdout: 56./zig-out/bin/zsmtp serve 2525 57 58# Send a message read from stdin: 59printf 'Subject: hi\r\n\r\nhello\r\n' | \ 60 ./zig-out/bin/zsmtp send 127.0.0.1 2525 me@example.com you@example.net 61``` 62 63## Status 64 65Plaintext SMTP only for now — STARTTLS/implicit TLS is the next planned step 66(the transport-agnostic design is meant to make that a drop-in layer). 67Not yet implemented: TLS, streaming (non-slice) message bodies, AUTH beyond 68PLAIN, and ESMTP parameter handling (SIZE=, BODY=) on the server side. 69 70## Tests 71 72```sh 73zig build test 74```