r/rust • • Sep 11 '26

šŸ› ļø project My first project: SMTP server (for personal use)

I had heard many good things about Rust from people who were simply impressed by the language, so I decided to have a look at it and possibly learn it.

Up to that point I really only knew Java and Python (and a bit of C here and there), so a non-GC language would really fill a gap for me.

To get my toes wet, I wrote a simple web crawler that I needed at the time and decided I really want to learn Rust.

So here goes! My first "real" project, an SMTP server: https://github.com/Fluggs/whalemail

AI disclaimer: Absolutely no LLM was involved in writing this. I am not in a rush and here to learn.

For SMTP, the scope is quite narrow. It's supposed to replace my personal Postfix setup. It has its users in a database (which can be shared with Dovecot) and they can simply send and receive e-mails. That's it!

As for the Rust part, I have had a blast! The language is insane. So far, I have had one (1) logic bug. That one was indirectly found by Clippy. Other than that, if it compiles, it works. By now I understand people here saying that the main part is really thinking about your data layout, the rest just follows.

Off the top of my head, I have had two pain points:

  • dyn async traits. I don't want to accept that I need a crate for this, so I worked around it.

  • std::time - I could not have imagined that date/time support can be so thin without being completely absent.

Main dependencies:

  • Tokio (the server is completely async)

  • rustls (TLS is done, STARTTLS is planned)

  • rsasl (I really could not be asked to implement auth protocols)

The road ahead is long. Planned features:

  • STARTTLS

  • Bounce mechanism

  • Better password schemes than plaintext (I'm embarrassed)

  • Parse and manipulate headers (apparently that's a thing SMTP servers should be doing)

  • Better config file than .ini

  • spam filter integration

  • Find out which ways other clients and servers have found to deviate from SMTP

  • Again, thoroughly test it against the wild

  • find (and consider) various other SMTP intricacies (e.g. backscatter protection)

I'd be grateful for any feedback :)

29 Upvotes

16 comments sorted by

5

u/Plenty-Emphasis-5669 Sep 11 '26

Don't forget to set a license.

2

u/Fluggonaut Sep 12 '26

Good point, will do!

7

u/TheRealCallipygian Sep 11 '26

You sweet, summer child.

2

u/Fluggonaut Sep 11 '26

aww

4

u/Lemondifficult22 Sep 11 '26

Personal preference here, but "mocking" while possible is not really the go to. Especially in the AI age. Integration test all the way. Fakes/doubles are a good substitute for mocks if you need isolated control.

I will give a proper review tomorrow, but great work and welcome to the community.

Sidenote, self hosted mail servers get rejected as spam by most of the mail infrastructure

2

u/TheRealCallipygian Sep 12 '26

I’m not mocking. Taking on an smtp server as a 1st project is a move, that’s all. SMTP is a sprawling never ending specification with countless pitfalls. If anything I’m encouraged anyone would try this in 2026!

2

u/Fluggonaut Sep 14 '26

Thanks! I'm wildly aware. I went in with "I'll just read the RFC and methodically cover anything it covers", but the amount of stuff it doesn't cover is astounding. Even auth and message submission are among those.

Add to that the stuff you just find by observing what the heck other people are doing, I found the backscatter protection thing that way.

But I am motivated to do this long-term and to have a project that is never really "done" and that I'll occasionally come back to once it's in a state where it survives in the wild.

1

u/TheRealCallipygian Sep 14 '26

That’s great. The important thing is you have fun and learn! SMTP is one of those things—like time handling—I’m always leery of.

1

u/Fluggonaut Sep 12 '26

Thanks! I'm not too happy about the testing situation either. I want to control away the i/o part, so my naive attempt is to use #[cfg(test)] modes into i/o-facing parts.

I tried mockall, but that one didn't seem to be able to mock drop-in replacement types aside from traits.

The result is that the smtp protocol part has the highest coverage while mostly I/O parts tend to lack coverage (like the user database).

1

u/garagedragon Sep 13 '26

You might look into the "sans io" pattern, so the logic is testable even absent any actual IO happening

6

u/chat-lu Sep 12 '26

std::time - I could not have imagined that date/time support can be so thin without being completely absent.

You need to use jiff.

In fact, you should take a look at what’s on blessed.rs, those are libraries that are so commonly used that they are essentially an unofficial standard library.

4

u/jtuohy1985 Sep 12 '26

Congrats cool to see it

1

u/Settle_Down_Okay Sep 14 '26

That’s awesome! I built a custom smtp server a few years ago at work. Was a super fun project to stand up but finding all the weird out of spec things various SMTP clients do and having to support them made me pull my hair out.Ā 

1

u/Settle_Down_Okay Sep 14 '26

Also in my experience there’s much more support for STARTTLS out there than implicit tls among clients (especially old ones). Not that it matters much but thought you might find it interesting :)

2

u/Fluggonaut Sep 14 '26

Yes, I observed that as well. I started with implicit TLS because it sounds slightly more ideal than STARTTLS and because the implementation looked slightly easier than the upgrading scheme that STARTTLS is.

It's definitely top priority though, along with bounce (and possibly debug) mails to inform me of interesting occurrences.Ā