My comment box is your email client | Andros Fenollosa
My blog has no comment form: no text box, no captcha, no third-party JavaScript widget. And yet, any reader can comment on an article, reply to another comment and get notified when someone answers them. The trick is using email as the user interface.
Let me start with a disclaimer: I am well aware that not everyone has the technical skills to build a system like mine, and I am not trying to fool anyone by sugarcoating its implementation. This is aimed at the 0.1%, those crazy people who embrace the ideas of an open, universal web where you own your data. That said, I will leave the pieces behind as code examples, so anyone who wants to try has a place to start.
I can tell you upfront that you need nothing exotic: a mailbox, a filter in your email provider, a database, a couple of scripts and a cron job. My site is built with Django LiveView, with Huey as the background task manager, but the examples will be in Flask: less magic, fewer lines and easier to translate to your favorite framework.
But first we have to answer a question.
Why email?
We cannot go on without listing the advantages it has over a traditional web form:
I avoid building a form and a backend with all the complexity that entails: validation, bot protection, error messages, and so on.
I get a direct communication channel with the reader: I can notify them by email when someone replies, and they can reply to me. I can even start a private conversation if I want to.
I ask for no registrations or user accounts: the reader does not have to create a profile, remember a password or fill in a captcha.
The reader writes comfortably and from wherever they want: their favorite email client, with their spell checker and their keyboard shortcuts, whether in the terminal, on their phone or in a webmail.
Spam filtering is handled by my email provider, which has spent decades perfecting it. I do not have to reinvent the wheel.
Receiving an email is more reliable than a web form submission: my backend can be down or overloaded, but the email will keep retrying until the server is available again. No data is lost.
Besides, there is no input more simple, familiar and accessible than email.
There is a deeper reason too: comments are content as well, and the IndieWeb principle of own your data applies to them just the same. If they live in a third-party service like Disqus and it shuts down, changes its terms or kicks you out, it takes years of conversation with it. In my database and under my domain, I am their custodian. And this channel coexists peacefully with webmentions, which collect what other websites say about my articles: email collects everyone else. I told you about it in I joined the IndieWeb, here's what I learned.
Of course, I still have to maintain a database to store the comments and their relationships, with a minimal backend to render them. It is not a serverless system.
The architecture in one minute
The whole system boils down to four pieces talking to each other:
sequenceDiagram<br>participant R as Reader<br>participant M as Mailbox<br>participant T as Scheduled task<br>participant D as Database<br>R->>M: Email to comment+article-{uuid}@your-domain.com<br>M->>M: The filter moves it from the inbox to website/comments<br>T->>M: Every 5 minutes it reads the folder over IMAP<br>T->>D: Turns the email into a comment<br>T->>M: Deletes the processed email<br>D->>R: The article renders the comment with its reply address<br>opt Replying to a comment<br>R->>M: Email to comment+reply-{id}@your-domain.com<br>T->>R: Notification email to the author of the parent comment<br>end
Each article generates a unique email address and shows it on the page, inviting readers to leave a comment.
The reader writes an email to that address, with their comment in the body of the message. The subject and any HTML are ignored: only the plain text matters.
A filter in the email provider moves those emails from the inbox to a specific folder. For example website/comments.
A scheduled task reads that folder over IMAP every 5 minutes.
If it finds an email, it turns it into a comment in the database and then deletes the processed email.
The comment is rendered on the article page with a new unique email address to reply to it.
And if someone replies to a comment, an email is sent to the author of the parent comment, letting them know that someone has answered.
Now let's look at each piece in depth.
An address for every article
The first piece is plus addressing, also called subaddressing: almost every email provider (Fastmail, Gmail, Proton...) delivers any email sent to user+whatever@domain.com to the mailbox of user@domain.com. There is no need to create any new address: the suffix after the + is free and infinite.
That lets me encode the destination of the comment inside the address itself:
comment+article-{uuid}@your-domain.com to comment on the article with that identifier.
comment+reply-{id}@your-domain.com to reply to...