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:

  1. a way to receive mail from other servers
  2. a way to deliver or store mail for local users
  3. a way for users to read their mail
  4. a way for users to send mail

Arguably, the current codebase now accomplishes all four.

  1. The server can receive mail using a SMTP server.
  2. Received mail is stored in a Maildir-formatted inbox.
  3. Users can retrieve their mail from the server using POP3.
  4. 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:

  1. Update tcp.c to allow for a shared TLS-aware connection layer
  2. Verify remote certificate with outbound STARTTLS
  3. Support STARTTLS for inbound SMTP
  4. Support STLS for POP3 (RFC 2595)
  5. 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 IMAP (RFC 3501)  
  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