· 6 min de lectura

Papiro: leer la web en la Kindle antes de dormir

Diseño de un servicio que convierte links en artículos de diario y los manda a la Kindle, con Go, un agente editor y cero costo.

Este post es el diseño de un proyecto que todavía no existe. Lo escribo antes de escribir la primera línea de código porque obliga a responder las preguntas incómodas temprano: qué modelo, dónde corre, cuánto cuesta, qué puede salir mal.

El problema

En realidad son dos, y uno alimenta al otro.

1. Guardo más links de los que alcanzo a leer. Durante el día me cruzo con posts técnicos, noticias, un instructivo para algo que quiero hacer el fin de semana. Los abro, los dejo en una pestaña o en “leer después”, y ahí se quedan. No es falta de interés: es falta de un momento tranquilo para leerlos.

2. Ese momento tranquilo es la noche, y lo arruino con el celular. Cuando me acuesto, agarro el celular para ponerme al día con todo lo que guardé. Pantalla brillante, notificaciones, un link que lleva a otro. Después de un rato con esa pantalla, me cuesta dormirme.

La Kindle resuelve el segundo problema por cómo está hecha: tinta electrónica, sin notificaciones, sin pestañas. Lo que falta es un puente cómodo entre “este link me interesa” y “está en mi Kindle, listo para leer”.

La idea: Papiro

Un servicio en papiro.duv0-x.cloud con una sola caja de texto, como un buscador:

  1. Pego la URL del artículo.
  2. Un agente editor lee el contenido y lo reescribe como un artículo de diario: limpio, en español, con titular, bajada, tiempo de lectura y la fuente original.
  3. El servicio arma un EPUB con estética de periódico y lo manda por correo a mi Kindle.

En la noche, en vez del celular, agarro la Kindle y ahí está la “edición nocturna” con lo que guardé durante el día.

Maqueta de la pantalla inicial de Papiro: una caja tipo buscador para pegar la URL y el correo de la Kindle
Maqueta en Stitch, con el mismo sistema de diseño que este blog

Mientras el agente trabaja, la página muestra una barra de progreso con algo de humor: una rotativa en ASCII que imprime la primera plana y avanza con las etapas reales del proceso.

Arquitectura

navegador ──▶ Cloudflare (DNS · Turnstile) ──▶ Cloud Run: servidor Go
                                                 │
                         1. descarga la URL      │  (protección SSRF)
                         2. extrae el contenido  │  go-readability
                         3. agente editor        │  Ollama Cloud → JSON
                         4. arma el EPUB         │  html/template + go-epub
                         5. envía a la Kindle    │  Resend (@papiro.duv0-x.cloud)
                                                 ▼
                                      tu-kindle@kindle.com

Todo el trabajo ocurre dentro de una sola petición. El navegador la abre con Server-Sent Events y el servidor va emitiendo cada etapa a medida que termina. Así la barra de progreso muestra avances reales, sin cola de trabajos, sin base de datos y sin polling.

Las decisiones

EPUB, no PDF

La primera intuición fue un PDF con diseño de periódico. Pero un PDF es una foto fija: en una pantalla de 6" no se puede agrandar la letra, subrayar es torpe y las columnas obligan a hacer zoom. Un EPUB se convierte al formato nativo de la Kindle cuando entra por Send to Kindle. El texto fluye, puedo elegir tipografía y tamaño, subrayar y buscar palabras en el diccionario.

La estética de diario se logra con CSS dentro del EPUB: cabecera con el nombre de la “publicación”, filetes, serif, capitulares, bajada en cursiva. La Kindle no respeta todo el CSS, así que el diseño tiene que verse bien aunque se pierdan algunas reglas.

Extraer antes de pensar

No le paso el HTML crudo al modelo. Primero, un port de Readability en Go (go-readability) saca el contenido principal: sin menús, banners de cookies, comentarios ni “artículos relacionados”. El agente recibe texto limpio, lo que significa menos tokens, menos ruido y menos alucinación. La parte determinista la hace el código y la de criterio, el modelo.

JSON, no TOON

Pensé en usar TOON como formato intermedio. TOON ahorra tokens cuando la entrada es tabular: muchos objetos con los mismos campos. Un artículo no es eso: es prosa. Para la salida del agente importa más que la estructura sea confiable, y ahí gana JSON con un schema. Ollama permite exigir un JSON Schema en la respuesta, así que el modelo devuelve exactamente esto:

type Article struct {
	Title       string    `json:"title"`
	Dek         string    `json:"dek"`          // bajada
	Byline      string    `json:"byline"`
	SourceURL   string    `json:"source_url"`
	ReadingMins int       `json:"reading_mins"`
	Sections    []Section `json:"sections"`
}

type Section struct {
	Heading    string   `json:"heading,omitempty"`
	Paragraphs []string `json:"paragraphs"`
	PullQuote  string   `json:"pull_quote,omitempty"`
}

Ese struct es el contrato entre el agente y el resto del sistema. El EPUB se renderiza con html/template a partir de él, y el modelo nunca escribe HTML.

El editor: un modelo de Ollama Cloud

Ya pago Ollama Cloud, así que el agente editor corre ahí, sin costo adicional. La instrucción es de edición fiel: conservar las ideas, la estructura y los datos del original, quitar el relleno, traducir al español si hace falta y nunca inventar. Antes de elegir el modelo, voy a comparar varios con un conjunto fijo de artículos de distintos idiomas y largos. Lo que sí sé es que hay que evitar los modelos que gastan el presupuesto de tokens “pensando” y devuelven una respuesta vacía.

Go en Cloud Run, Cloudflare al frente

El backend es Go con Gin, Templ y HTMX: el HTML se renderiza en el servidor, igual que en proyectos anteriores. Quería que todo viviera en Cloudflare, pero los números no dan:

OpciónProblema
Workers (plan gratis) con Go en Wasm10 ms de CPU por petición y scripts de máximo 3 MB. Parsear HTML y armar un EPUB no cabe en esos límites.
Cloudflare ContainersCorre el binario de Go tal cual, pero requiere el plan Workers Paid (USD 5/mes).
Cloud RunEl plan gratuito sobra para uso personal, escala a cero y acepta peticiones de varios minutos.

Cloudflare se queda con lo que mejor hace: DNS, el subdominio y Turnstile.

El correo: Resend

Cloudflare tiene envío de correo, pero en el plan gratuito solo llega a direcciones verificadas, y una dirección @kindle.com no puede hacer clic en un link de verificación. Resend tiene un plan gratuito más que suficiente y verifica el subdominio papiro.duv0-x.cloud con registros DNS en Cloudflare. Los EPUB salen desde kindle@papiro.duv0-x.cloud, que hay que agregar una vez a la lista de correos aprobados en la cuenta de Amazon.

Seguridad: un formulario que manda correos

Un servicio público que recibe una URL y envía un correo tiene dos riesgos obvios:

  • Spam. Solo se envía a direcciones @kindle.com que estén en una lista blanca. El formulario está protegido con Turnstile, el sistema antibots de Cloudflare, que casi siempre se resuelve sin que el usuario haga nada, y hay un límite de envíos por día.
  • SSRF. El servidor descarga URLs arbitrarias. Hay que bloquear IPs privadas y de metadatos (169.254.169.254, 10.0.0.0/8, localhost…), resolver el DNS y validar la IP antes de conectar, y limitar el tamaño y el tiempo de la descarga.

Costo

PiezaServicioCosto
Dominio, DNS, antibotsCloudflareUSD 0
Backend GoCloud Run (plan gratuito)USD 0
Agente editorOllama Cloud (suscripción existente)USD 0 adicional
CorreoResend (plan gratuito)USD 0

Lo que sigue

  • Comparar modelos de Ollama Cloud para elegir el editor.
  • Diseñar el CSS del EPUB y probarlo en una Kindle real, porque el emulador miente.
  • Decidir qué hacer con las imágenes del artículo original: incluir solo la principal, todas, o ninguna.
  • Manejar los paywalls: si el contenido no se puede extraer, el servicio debe decirlo en vez de inventar.

Cuando esté construido, el siguiente post contará cómo salió, incluido en qué se equivocó este diseño.