Rebuilding Email for the Modern Web: An HTTPS-First Prototype

A technical look at an HTTPS-first email prototype, its JSON API, and the protocol, identity, delivery, and security questions that remain before such a design could interoperate with email.

Rebuilding Email for the Modern Web: An HTTPS-First Prototype

Email is a dinosaur.

Let’s just get that out of the way. It’s a magnificent, resilient, and shockingly useful dinosaur that somehow survived the meteor. But it’s still a dinosaur.

SMTP, POP3, and IMAP were designed for a different internet, with distinct roles for transferring and accessing messages. They remain widely interoperable, but they also bring operational complexity.

For months, I’ve been haunted by a question.

What if we built email today? From scratch. Using the tools we have now.

This question took over. It stopped being a hypothetical and became an obsession. I tore down the old structure in my mind and started building.

The result is a full-stack prototype for a web-native mail system. A slick React/Next.js frontend. A powerful Python FastAPI backend.

And here’s the kicker. There is no SMTP server. No IMAP daemon. No POP3 listener.

The prototype uses one main transport. HTTPS.

This is the story of that prototype. It is an argument for exploring a web-native design, not a claim that the prototype replaces the open email ecosystem.


The Ghosts in the Machine

To build something new, you have to respect the old. Even if you plan on tearing it down.

Traditional email uses several protocols with different jobs.

SMTP (Simple Mail Transfer Protocol) This is the transfer protocol. Its job is to move mail from one server to another through a text-based command and reply exchange. HELO MAIL FROM: RCPT TO: DATA QUIT Simple. SMTP can be used with TLS, but SMTP and TLS solve different problems. TLS can protect a connection hop; it does not by itself provide end-to-end encryption for the message. The IETF’s RFC 8314 recommends TLS for email submission and access while noting that those recommendations are not a substitute for end-to-end encryption.

POP3 (Post Office Protocol) POP3 is a retrieval protocol for accessing messages held in a server maildrop. A client often downloads messages and may delete them, but the protocol is not designed for rich server-side mailbox synchronization. That is why it does not provide the same multi-device model as IMAP.

IMAP (Internet Message Access Protocol) This was the fix. The clever one. IMAP provides server-side mailbox access and synchronization, so a message can keep state across clients. It is stateful and operationally more involved than a single JSON endpoint, but that complexity supports mailbox semantics rather than being an accidental extra.

The core issue is not literally three different servers. SMTP, POP3, and IMAP are separate protocol roles, and a deployment may combine them or expose them through different services. Transfer, submission, and mailbox access still need clear boundaries, queues, retries, authentication, and abuse controls.

old mess


The Lightbulb Moment: A Lesson from the Fediverse

Before I show you the new way, let’s talk about Mastodon. Mastodon is a decentralized social network. Thousands of independent servers, all talking to each other. A user on mastodon.social can follow a user on tech.hub. Seamlessly.

How? Does it use some secret, complex “Social Media Transfer Protocol”? No. It uses ActivityPub.

ActivityPub uses ActivityStreams 2.0 data and defines both a client-to-server API and a federated server-to-server API. HTTP carries the requests, but the specification also defines actors, inboxes, outboxes, object discovery, and delivery behavior. Authentication and signature choices are part of the federation security problem; JSON over HTTPS is not the whole protocol.

When an actor publishes an activity, a server delivers it to recipients’ inboxes according to the federation rules. The body can be a JSON representation of an ActivityStreams object.

This was the revelation. The Fediverse shows one way to build decentralized communication on web protocols, with semantics beyond the HTTP request itself. It uses existing web mechanisms while still defining protocol-specific behavior.

If a decentralized social network can use HTTP requests for federation… Why not explore a similar transport for a bounded messaging prototype?


The Blueprint: My HTTPS-First Mail System

So I built it. I took the idea of using web-native requests and applied it to a bounded email prototype. The entire architecture rests on two pillars. JSON for the data. HTTPS for the transport.

Here’s how an email lives and breathes in my system.

The Data: A Clean, Strict EmailModel

First, no more parsing arcane MIME headers. An email is a clean, predictable JSON object. Defined in my FastAPI backend with Pydantic, it looks something like this:

{
  "from": { "id": "sujal", "host": "my-server.com" },
  "to": [{ "id": "bob", "host": "another-server.com" }],
  "subject": "This is the future",
  "body": "The email body, as a simple string.",
  "attachments": [
    {
      "hash": "a1b2c3d4...",
      "filename": "project_plan.pdf",
      "mimeType": "application/pdf"
    }
  ],
  "date": "2025-03-15T10:00:00Z"
}

This is the prototype’s internal message shape, not an interoperable email format. The frontend sends it. The backend understands it. It is simple and extensible for this experiment, but a real interoperable format would need explicit versioning, validation, identity, and attachment semantics.

The Send-Off: An API POST Request

This is the heart of it all. When you click “Send” in my React app… No connection to port 25. No HELO greeting. No text-based dance.

The prototype can make one API call for its local send path. POST /email/send

The body of that request is the EmailModel JSON object. The sketch uses a bearer token placeholder: Authorization: Bearer [token]. That authenticates an API request only if the server validates it; server-to-server identity, key rotation, authorization, and replay protection still need a design. It is clean to prototype. It is not automatically secure just because it uses HTTPS.

old mess

The backend send_email function then acts as the new postman. For each recipient, it looks at their host. And it makes its own HTTPS request.

import requests

# A simplified look inside my email_service.py
def send_email_to_server(email_data):
    recipient_host = email_data["to"][0]["host"]
    url = f"https://{recipient_host}/email/receive"
    
    # Simplified: a real implementation would handle every recipient.
    # It would also need authentication, timeouts, retries,
    # idempotency, and durable failure handling.
    requests.post(url, json=email_data, timeout=10)

The server-to-server transfer is no longer a cryptic chat. It’s a modern API call.

The Arrival: A Smarter Mailbox

In the prototype, the recipient’s server exposes an HTTPS endpoint such as /email/receive. HTTPS provides transport; it does not by itself prove that the sender is authorized or that the message will be accepted exactly once.

When the POST request from the sender’s server arrives, the prototype does a few simple things.

  1. Validates the JSON against its own EmailModel.
  2. Checks if the recipient is a local user.
  3. Adds its own metadata, like a deliveredAt timestamp.
  4. Saves the entire JSON object to the user’s inbox folder. _data/inbox/bob/a4b5c6d7.json

Done. The email is delivered. A production design would also need an explicit trust model, recipient discovery, replay protection, queueing, retries, idempotency, spam controls, and a delivery guarantee. This path is easy to explain, but it is only a prototype flow. HTTPS alone does not establish end-to-end security, successful delivery, or interoperability.


The Superpowers of a Modern Architecture

This isn’t just about replacing old tech with new tech. This is about unlocking capabilities that were nightmares to implement before.

End-to-End Encryption Is a Separate Design

The body of my email is just a string. Before sending, a client could encrypt it.

The JSON payload would look like this:

{
  "body": null,
  "encryptedBody": "U2FsdGVkX1+aBc...",
  "encryptionAlgorithm": "AES-256-GCM",
  "nonce": "..."
}

This payload is incomplete: authenticated encryption also requires a nonce or IV, and users need an authenticated key-distribution scheme. Without those pieces, the field does not provide end-to-end encryption. The server would not need to see plaintext, assuming the clients authenticate keys correctly. It could store and deliver the ciphertext without being able to read it.

Only clients holding the right keys could decrypt the message.

End-to-end encryption is possible in this architecture, but it is not automatic. Key distribution and authentication are part of the architecture too.

transparent encryption

One Channel to Rule Them All

Right now, my system sends EmailModel objects. But what if we sent something else?

A chat message:

{
  "type": "chat_message",
  "from": { "id": "sujal", "host": "my-server.com" },
  "text": "Hey, you free?"
}

A calendar invite:

{
  "type": "calendar_invite",
  "organizer": { "id": "sujal", "host": "my-server.com" },
  "title": "Project Sync",
  "startTime": "2025-03-18T14:00:00Z"
}

The same transport could carry different application messages if both sides agree on schemas, permissions, delivery semantics, and versioning. Email. Chat. Calendars. File transfers.

That could reduce duplicated transport code inside one ecosystem, but it would not eliminate XMPP, CalDAV, or other protocols for existing clients and interoperability. It would be a new, bounded protocol rather than a universal replacement for the existing ones.


What This Prototype Makes Easier

The comparison below is between this prototype’s API path and one SMTP submission path. It is not a proof that the two systems solve the same interoperability problem.

Authentication:

  • Prototype: A bearer token can authenticate an API request, but the design still needs token storage, rotation, authorization, and server-to-server authentication.
  • SMTP: SMTP has authentication extensions and can be used with TLS; the exact security depends on the deployment and the negotiated mechanisms.

Data:

  • Prototype: JSON gives the API a schema for this experiment.
  • SMTP: SMTP transfers mail; message structure and attachments are handled by related message formats such as MIME. It is not accurate to compare SMTP itself with a JSON document.

Error Handling:

  • Prototype: HTTP status codes can describe an API request, but durable delivery still requires queueing, retry, duplicate handling, and clear failure semantics.
  • SMTP: SMTP uses standardized reply codes, and capable implementations are expected to queue and retry messages when delivery cannot complete.

Infrastructure:

  • Prototype: HTTPS commonly uses port 443, but deployment still requires certificates, server authentication, recipient discovery, and reachable endpoints.
  • SMTP: SMTP deployments use different service roles and ports; port 25 filtering varies by network and provider, and SMTP’s relay model is what enables interoperability.

The prototype is easier to explain, but it is not a drop-in replacement for interoperable email.


A Call for a New Beginning

I started this project out of frustration. Out of a belief that we could do better. It has now become a conviction.

The protocols of the past served us well, and their age alone does not make them obsolete. They embody interoperability, queuing, retries, mailbox access, abuse controls, and decades of deployment knowledge.

We have the tools. We have the patterns. We have the web.

My project is a working prototype for exploring a different trade-off: a web-native API for a bounded ecosystem. Before it could claim to replace email, it would need a threat model, authenticated identity and discovery, key management, reliable delivery semantics, spam and abuse controls, durable storage, and backward compatibility.

It is worth building what comes next, as long as the missing protocol work is part of the design rather than hidden behind a simple POST request.

old mess

Older writing

Also read

A Day of Unexpected Messages: My Experience with a Side Project and a Sudden Worry