Sent from my terminal
This is a follow up (part 7) to How to send an email.
At this point, we can successfully send and receive emails; user authentication is handled correctly; and, we concurrently run a SMTP client, SMTP server, and POP3 server.
In the first page of this series (Minimum viable email service) I outlined the following 4 requirements to satisfy my vision of a "Minimum viable email service":
At minimum, a working email server requires four fundamental capabilities:
- a way to receive mail from other servers
- a way to deliver or store mail for local users
- a way for users to read their mail
- a way for users to send mail
Arguably, the current codebase now accomplishes all four.
- The server can receive mail using a SMTP server.
- Received mail is stored in a Maildir-formatted inbox.
- Users can retrieve their mail from the server using POP3.
- Users can send mail (although this currently requires connecting to the VPS first since we are only permitting localhost-sent messages).
There are two major limitations: firstly, we are greatly overly optimistic. We do not authenticate the senders of mail we are receiving and cannot receive mail with TLS. Secondly, forcing local users to remote into a VPS to send their email is obviously ridiculous for more than a single local user.
The goal of this webpage is to address both of these issues, adding a layer of "skepticism" to the emails with receive and allowing for authenticated users from outside of the VPS itself to verify their account and send email.
More TLS, less problems
In order to send email without remotely connecting to the VPS, I need to support SMTP Submission (RFC 4609); however, before implementing SMTP Submission, I first wanted to update the TLS connection code, extracting it from outbound.c and integrating into the shared TCP server in tcp.c. This way, I could maintain a single, TLS-aware connection layer.
I think the actual implement is rather mechanical and, simply, boring (and that's coming from a guy who just spent 3 weeks building an email service) so I'll just provide an even-more-high-level summary than normal:
- Update
tcp.cto allow for a shared TLS-aware connection layer - Verify remote certificate with outbound STARTTLS
- Support STARTTLS for inbound SMTP
- Support STLS for POP3 (RFC 2595)
- Implement SMTP Submission (RFC 4609) for local users to send mail
Then, we need an actual TLS certificate and key pair. I used openssl to generate one using RSA:
openssl req -x509 -newkey rsa:2048 \ -keyout tls_key.pem \ -out tls_cert.pem \ -days 365 \ -nodes \ -subj "/CN=mail.pokey.onl"
SMTP Submission
In order to use the newly-implemented SMTP Submission standard, I installed msmtp by Martin Lambers, the same creator behind mpop which I am using to retrieve mail with POP3.
The configuration format for msmtp is similar to that of mpop. To begin, I wrote the following to ~/.msmtprc:
account bobatpokey host mail.pokey.onl port 587 from bob@pokey.onl auth on user bob password password tls on tls_starttls on account default : bobatpokey
When trying to use this configuration, I ran into an expected error:
msmtp: TLS certificate verification failed: The certificate is NOT trusted. The certificate issuer is unknown.
In order to convince msmtp to trust my self-signed cert, I set tls_trust_file (within ~/.msmtprc) to a path with the server's certificate file. Then, simply running
echo "Subject: Test\n\nHello world" | msmtp alice@pokey.onl
sent an email to alice@pokey.onl with the content in the string. Logging into the VPS, I could see that the message was delivered to alice@pokey.onl's mailbox as ./mail/alice/Maildir/new/1776063550.139702593.238350.139846036358848.mail.pokey.onl with content:
Received: from localhost ([REDACTED])
by mail.pokey.onl with ESMTPS;
Mon, 13 Apr 2026 06:59:10 +0000
From: bob@pokey.onl
Date: Mon, 13 Apr 2026 01:59:09 -0500
Message-ID: <049f8be0f2df155ea9ec0295aad36d47@pokey.onl>
Subject: Test
Hello world
This demonstrates the minimum-viability of sending mail locally using SMTP Submission with my email service.
I also went back to update ~/.mpoprc so that fetching an email inbox is done over TLS; I simply updated tls to on (instead of off) and provided the new path for tls_trust_file. This way, both sending and receiving mail is done via TLS connections.
Results
| Capability | Standard/support | Status |
|---|---|---|
| Receive mail | SMTP (RCC 5321) | Implemented |
| IMF (RFC 5322) | Implemented | |
| MIME (RFC 2045-2049) | Supported | |
| Store mail | Local user accounts | Implemented |
| Maildir | Implemented | |
| Read mail | ||
| POP3 (RFC 1939) | Implemented | |
| Send mail | SMTP Submission (RFC 6409) | Implemented |
| STARTTLS (RFC 3207) | Implemented | |
| SPF, DKIM, DMARC, TLS | Implemented |
The project now meets all of the goals that I had originally set out! Despite this, I still think there are some further directions I could explore. For example: IMAP, implicit TLS ports, ipv6 support, fixing bugs, user creation/management, or webmail/mail client.
Last updated April 13, 2026