Fue Top 15 en Hacker News

El email moderno se puede montar con piezas de otros

Read in English

Vamos a diseñar el sucesor del email sobre HTTP, arreglando los fallos de diseño que SMTP arrastra desde hace 40 años. Pieza por pieza. El objetivo no es sustituir el sistema actual de correo, ¡Dios me libre!, sino aprender, divertirnos y descubrir tecnologías actuales para reemplazar cada elemento: enviar, recibir, pasarela, claves, etc. El sistema nunca se entenderá con Gmail ni con ningún proveedor de correo clásico: solo habla consigo mismo. Lo único que vamos a conservar es la forma de las direcciones usuario@dominio. Todo lo demás se reinventa.

Como es un sistema de correo sobre HTTP, un buen nombre para el protocolo sería HMTP: Hypertext Mail Transfer Protocol (la S de Simple de SMTP cede su sitio a la H de HTTP).

Ahora toca preparar el stack tecnológico: HTTP, TLS, WebFinger, ActivityPub, Webmention, Ed25519, HPKE, Sigchains, etc.

La lista de materiales

Este diseño no inventa una sola tecnología: todo existe ya.

Problema Tecnología existente Quién la usa hoy
Transporte y códigos de estado HTTP Toda la web
Cifrado de transporte TLS + Let's Encrypt Toda la web
Descubrimiento de usuarios WebFinger (RFC 7033) Mastodon y el Fediverso
Entrega de mensajes POST a un inbox ActivityPub
Verificación del remitente en origen El patrón de Webmention y DKIM IndieWeb, todo el email
Firmas Ed25519 SSH, Signal
Cifrado del contenido HPKE (RFC 9180) MLS, TLS ECH
Identidad que sobrevive a rotar claves Sigchains ATProto (Bluesky), Keybase
Lectura y sincronización JMAP (RFC 8620) Fastmail
Notificaciones push SSE / WebPush Todos los navegadores
Consentimiento en primer contacto Message requests Signal, Instagram
Adjuntos por referencia con hash Content addressing Git, IPFS, Matrix

Cada pieza que vamos a usar está estandarizada, desplegada y probada a escala por millones de personas; lo único nuevo es el ensamblaje.

Elegir HTTP no es solo pragmatismo. Resuelve de saque varias cosas que cualquier protocolo nuevo tendría que montar en algún momento:

  • TLS, virtual hosting y SNI: gratis.
  • Códigos de estado: el catálogo que necesita un protocolo de correo ya existe en HTTP. 202 Accepted (entregado a cola), 429 Too Many Requests + Retry-After (control de ritmo), 404/410 (buzón inexistente/desaparecido), 3xx (buzón mudado), 413 (demasiado grande). Hasta el anti-spam de pago tiene código reservado desde 1997: 402 Payment Required.
  • Infraestructura existente: proxies, balanceadores, Nginx, librerías en todos los lenguajes. El protocolo deja de ser un servidor nuevo y pasa a ser una convención sobre HTTP, como Webmention o Micropub.

Pasemos al siguiente nivel: ¿cómo descubrir a un usuario, verificar su identidad, entregar el mensaje, firmarlo y cifrarlo, todo sobre HTTP?

1. Descubrimiento y delegación (un MX mejor)

La delegación se arregla con un documento estático:

GET https://example.com/.well-known/hmtp/ana
{
  "inbox": "https://mail.migadu.example/hmtp/inbox/ana",
  "keys": { ... },
  "devices": [ ... ]
}

El inbox puede vivir en otro host: eso es el registro MX, pero sin tocar DNS. Un blog estático en GitHub Pages puede delegar su correo a un proveedor sirviendo un JSON. Y permite algo que MX no permite: delegación por usuario (cada buzón de un dominio en un proveedor distinto). De hecho no haría falta inventar ni la ruta: WebFinger (RFC 7033) hace exactamente esto y Mastodon ya demostró que escala.

2. Identidad que sobrevive a la rotación de claves

La identidad no puede ser una clave (se pierden, caducan); tiene que ser algo que las claves respaldan. Propuesta de dos anclas:

  • Continuidad criptográfica: el documento de descubrimiento publica la clave actual y una cadena de rotaciones, donde cada clave nueva va firmada por la anterior. Quien te conocía con la clave N puede verificar la N+1 sin confiar en nadie. Es una sigchain, lo que hace ATProto con los DIDs o lo que hacía Keybase.
  • Control del dominio como respaldo: si pierdes la clave sin rotación firmada (te roban el portátil), el dominio declara una clave nueva sin cadena, con un periodo de anuncio obligatorio (digamos 30 días) durante el cual los servidores que te conocían muestran la advertencia "identidad reanclada por dominio, no por firma".

Sin embargo la identidad anclada al dominio tiene su propio talón de Aquiles, y es que un dominio no se posee, se alquila. Si dejas de pagarlo, caduca y alguien lo registra, el nuevo dueño publica sus claves en tu .well-known y desde ese momento recibe tu correo y firma como tú. Nadie tiene forma de distinguir al heredero legítimo del okupa. Es el problema que ATProto intenta resolver separando la identidad del dominio con DIDs, al precio de otra pieza de infraestructura. Sabemos que es un problema real y tiene una solución conocida. Ahora sigamos.

3. Entrega con cola (store-and-forward)

La entrega es un POST al inbox del destinatario:

POST /hmtp/inbox/ana HTTP/1.1
Host: mail.migadu.example
Content-Type: application/hmtp+json

La clave no es la petición, es quién lo hace. Tu cliente no entrega directamente al destinatario, sino a tu propio servidor (un POST autenticado a tu outbox), y es tu servidor quien encola, reintenta con backoff exponencial y respeta los Retry-After. Es admitir que la separación MUA/MSA/MTA de SMTP era correcta. Una genialidad silenciosa del email es que si el servidor destino está caído, tu servidor reintenta durante días y tú te olvidas.

Pero añadamos una mejora que SMTP nunca tuvo. Cada mensaje lleva un ID que es el hash de su contenido, así que los reintentos son idempotentes. El servidor receptor deduplica por ID y el clásico "correo duplicado porque falló el ACK" desaparece por construcción.

El ciclo completo de una entrega, con el nodo destino caído en el primer intento:

sequenceDiagram
    autonumber
    participant Ana as Cliente de Ana
    participant SA as Servidor de Ana
    participant SB as Servidor de Bob

    Ana->>SA: POST /outbox (mensaje firmado)
    SA-->>Ana: 202 encolado
    SA->>SB: GET /.well-known/hmtp/bob
    SB-->>SA: inbox y claves de Bob
    SA->>SB: POST /hmtp/inbox/bob (sobre + cuerpo sellado)
    Note over SB: caído: sin respuesta
    Note over SA: cola: reintenta con backoff exponencial
    SA->>SB: POST /hmtp/inbox/bob (reintento, mismo id)
    SB->>SA: GET /.well-known/hmtp/ana
    SA-->>SB: clave de firma de Ana
    Note over SB: firma verificada, deduplicado por id
    SB-->>SA: 201 delivered

Fíjate en que el diagrama contiene el protocolo entero: los dos GET al .well-known son el descubrimiento y la verificación, el POST es la entrega, y la cola vive donde debe, en el servidor del emisor.

4. Firmar y cifrar con las claves que ya tenemos

El mensaje es un objeto firmado, no texto suelto:

{
  "id": "sha256:9f2c...",
  "from": "ana@example.com",
  "to": ["bruno@example.org"],
  "date": "2026-07-26T10:00:00Z",
  "in_reply_to": "sha256:11ab...",
  "subject": "Re: aquella idea",
  "body": { "type": "text/markdown", "content": "<cifrado>" },
  "signature": "..."
}

De este modo ganamos muchas cosas:

  • Autenticidad en reposo: el correo almacenado lleva su prueba criptográfica. Un reenvío conserva la firma original. Falsificar el remitente se vuelve imposible incluso en cadenas de reenvíos.
  • Verificación del remitente sin DKIM: el servidor receptor hace GET al .well-known del dominio del from y comprueba que la clave firma. Es el mismo movimiento que la verificación de Webmention. La prueba se busca en la fuente.
  • E2E: el documento de descubrimiento publica claves de cifrado (X25519); el cuerpo va sellado con HPKE. El sobre (from, to, id, fecha) queda visible para enrutar y filtrar; el contenido, solo para el destinatario.
  • Hilos: in_reply_to y references por hash de contenido. Conversaciones reconstruibles sin heurísticas.
  • Adjuntos: fuera del mensaje. Un adjunto es {hash, url, size} apuntando al servidor del remitente; el receptor lo descarga bajo demanda y su servidor puede espejarlo. Se acabó el base64 inflando buzones.

5. Anti-spam por capas

Como cualquier sistema, es un problema complejo que requiere varias capas de defensa. Con HMTP podríamos empezar con tres:

  1. Coste de identidad: firmar como ana@example.com exige servir el documento de claves en example.com. La identidad está anclada a un dominio, y los dominios cuestan dinero. Es el coste sybil que las identidades autofirmadas no tienen. Sin embargo, un dominio da subdominios y buzones infinitos, así que el coste frena la creación masiva de identidades independientes, no de direcciones. Trabajamos a nivel de raíz.
  2. Consentimiento en primer contacto: un remitente desconocido no entra al buzón; entra a una bandeja de "solicitudes" con su primer mensaje visible (como los message requests de Signal). Aceptas y el hilo se abre para siempre. Un desconocido puede llamar a tu puerta, que es la propiedad esencial del correo, pero no puede llenarte el salón.
  3. Franqueo opcional para desconocidos: el servidor puede responder al primer contacto con 402. Configurable por buzón; el coste de spamear en frío deja de ser cero.

6. La lectura también se especifica

El email estandarizó el envío (SMTP) y la lectura (IMAP/POP) como mundos separados. Nosotros no necesitamos inventar nada: leer, sincronizar y buscar buzones sobre JSON/HTTP ya está resuelto y estandarizado por el IETF. Se llama JMAP. HMTP definiría la entrega; la lectura es JMAP con un tipo de objeto nuevo. Push al cliente con SSE o WebPush. El ciclo completo (enviar, entregar, leer, sincronizar) queda sobre HTTP.

La conclusión

Cada problema del correo tiene una solución desplegada y funcionando: el descubrimiento en Mastodon, la entrega en ActivityPub, la verificación en el IndieWeb, la identidad rotable en Bluesky, la lectura en Fastmail, el consentimiento en Signal. Una actualización del correo ya existe, pero nadie la ha ensamblado.

Solucionamos muchos problemas con soluciones elegantes y modernas:

  • Reputación y permiso social (SPF, DKIM, DMARC, PTR inverso, listas negras, meses de calentar una IP): sustituido por verificación criptográfica en cada mensaje, una firma y un GET al .well-known del remitente. Una propiedad que se verifica no necesita reputación.
  • Suplantación del remitente: imposible por construcción. El receptor comprueba la firma contra la clave publicada en el dominio del from, y la firma viaja con el mensaje incluso reenviado.
  • Delegación del correo (el registro MX): un documento estático en .well-known, con delegación por usuario y sin tocar DNS.
  • Privacidad del contenido (el PGP que nadie llegó a configurar): cifrado de extremo a extremo por defecto; el servidor receptor almacena un cuerpo que no puede leer.
  • Spam (filtros estadísticos a posteriori): consentimiento en primer contacto (los desconocidos llaman a la puerta, no entran al buzón), identidad con coste anclada al dominio y franqueo opcional con 402.
  • Duplicados por reintento: el id del mensaje es el hash de su contenido, así que los reintentos son idempotentes y el receptor deduplica por construcción.
  • Hilos reconstruidos con heurísticas: in_reply_to apunta al hash del mensaje padre; la conversación es un grafo verificable.
  • Buzones ligeros: adjuntos por referencia (hash + URL), descargados bajo demanda.
  • Infraestructura sencilla y conocida: todo viaja por HTTPS estándar, detrás del mismo Nginx y el mismo certificado que ya sirven tu web.
  • Rotar claves (la pesadilla operativa de DKIM): un comando. Como nadie fija tu clave, la nueva es confiable al instante y la robada queda inservible igual de rápido.

Hablar es fácil, por ello he implementado un prototipo funcional en Python: github.com/tanrax/hmtp. Todo en un solo archivo. El prototipo cubre el transporte completo: entrega firmada, verificación en origen, cifrado de extremo a extremo, consentimiento en primer contacto, deduplicación, hilos, cola con backoff exponencial y rotación de claves en un comando. Deja fuera, a conciencia, las piezas de la periferia: la cadena de rotaciones firmadas (los receptores consultan tu clave en vivo en cada entrega en lugar de fijarla, así que solo empieza a pagarse cuando los nodos cacheen claves), el franqueo 402, los adjuntos por referencia y la lectura JMAP. El README trae el quickstart (tu primer correo en dos minutos, escribiéndote a ti mismo), una demo de dos nodos intercambiando correo cifrado en tu máquina y la guía de producción completa, del DNS al systemd. Recuerda que es un experimento de diseño con criptografía sin auditar; no lo uses para secretos de los que dependa nadie. Aunque podría ser un sistema ligero de comunicación para el ecosistema que te venga a la cabeza.

Espero que hayas disfrutado el paseo. Y si llegas a enviar tu primer correo con HMTP, me encantaría que me lo contaras.

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Will you buy me a coffee?

This is how I keep writing without ads or paywalls.

Comments

There are no comments yet.

You may also like

Visitors in real time

You are alone: 🐱