Construí un foro sin interfaz, sin contraseñas y para la terminal

Read in English

Me gustan los foros. Debates, preguntas, respuestas, opiniones, etc. Esa sensación de comunidad asíncrona, donde las respuestas se meditan y el contenido perdura, que hace de internet un lugar interesante. Por ello me gusta leerlos y participar en ellos, pero nunca he diseñado ni construido uno. En mi tiempo libre empecé a abocetar uno y acabé construyendo mi humilde foro headless federado, con tecnologías simples, modernas y estándares abiertos.

Y por el camino aprendí mucho. Un foro parece sencillo, pero esconde los problemas clásicos de cualquier servidor. ¿Quién puede hacer qué? ¿Cómo separas a un moderador de un usuario normal? ¿Qué pasa cuando hay que banear a alguien? ¿Cómo evitas que te roben una cuenta? Roles, permisos, baneos y seguridad. Tuve que resolverlos uno a uno, y eso no se aprende leyendo.

Vengo a contarte cómo hice Tyrtulia.

Un foro sin interfaz

El servidor solo ofrece una API sólida y documentada. Es REST con hipermedia: el cliente solo conoce una ruta, GET /, y todo lo demás lo descubre siguiendo enlaces. Por debajo hay estándares muy aburridos:

  • RFC 9110: semántica HTTP, con ETag e If-Match para no pisar ediciones.
  • RFC 9457: errores.
  • RFC 7396: actualizaciones parciales con PATCH.
  • RFC 6570: plantillas de URL.
  • RFC 4287: feeds Atom.
  • RFC 9562: identificadores UUIDv7.
  • JSON Schema: validación.
  • Server-Sent Events: eventos en tiempo real.

No hay interfaz web, ni formularios, ni sistemas avanzados de autenticación, ni gestión de archivos (imágenes, adjuntos, etc.), ni nada que no sea estrictamente necesario para leer y escribir mensajes.

Por eso es muy ligero. Ocupa entre 60 y 70 MB de RAM y unos 50 MB de disco. Solo necesita Python y SQLite para funcionar.

Una API que se descubre sola

¿Cómo sabe un cliente qué puede hacer? Se lo dice el servidor con enlaces. Fíjate en la misma petición, GET /, hecha por un invitado (simplificada):

{
    "name": "Forum of example.org",
    "_links": {
        "self": {"href": "/", "method": "GET"},
        "latest": {"href": "/threads", "method": "GET"},
        "search": {"href": "/search{?q,author,in}", "method": "GET", "templated": true},
        "login": {"href": "/sessions", "method": "POST", "schema": "/schemas/session"}
    }
}

Y ahora por alice, con sesión iniciada:

{
    "name": "Forum of example.org",
    "_links": {
        "self": {"href": "/", "method": "GET"},
        "latest": {"href": "/threads", "method": "GET"},
        "search": {"href": "/search{?q,author,in}", "method": "GET", "templated": true},
        "createThread": {"href": "/threads", "method": "POST", "schema": "/schemas/thread"},
        "notifications": {"href": "/notifications", "method": "GET"},
        "events": {"href": "/events", "method": "GET", "type": "text/event-stream"}
    }
}

Si mañana cambio una ruta, ningún cliente se rompe, porque ninguno la conocía de antemano. Solo tiene que seguir los enlaces que le da el servidor.

¿Y por qué no usar uno que ya existe?

Soy consciente de que hay foros de software libre muy completos:

  • Discourse: el más popular hoy en día.
  • Flarum: en PHP con MySQL.
  • NodeBB: en Node.js, con MongoDB, Redis o PostgreSQL. Destaca por el tiempo real con WebSockets.

O el clásico de toda la vida: phpBB.

Todos son fantásticos, pero están diseñados para usarse desde un navegador web. Además, tienen un enfoque ambicioso, ideal para comunidades grandes, pero excesivo para un foro pequeño y ligero.

Eso sí, Tyrtulia no renuncia al navegador: se puede indexar en buscadores y cualquiera puede leerlo desde la web.

Sin contraseñas

Ni OAuth, ni OpenID, ni formularios de registro. La autenticación se hace de dos formas:

  • Tu usuario Unix: el cliente habla con el foro por un socket Unix. El sistema operativo le dice al foro qué usuario está al otro lado. No hay que registrarse ni iniciar sesión. Tu usuario es tu cuenta.
  • Clave SSH: el cliente se conecta por HTTP y el foro le envía un reto. El cliente lo firma con tu clave privada SSH y el foro lo verifica con tu clave pública.

No hay registro por HTTP. Si tienes usuario Unix en el servidor, ya tienes cuenta: se crea sola la primera vez que conectas por el socket. Desde ahí puedes añadir tus claves SSH para entrar desde fuera. Si no tienes usuario en la máquina, el administrador te crea una cuenta con tu clave pública desde la línea de comandos. En un tilde, tener usuario Unix ya es la invitación.

Fácil de usar, seguro y sin contraseñas que robar.

Qué cambia respecto a BBJ

La idea no es mía. Me inspiré en BBJ, un tablón de anuncios para la terminal pensado para comunidades pequeñas o clubes (tildes, pubnixes, servidores de clubes, etc.). Cogí su espíritu y lo llevé a mi terreno. Sin embargo, hay diferencias importantes.

Primero, los cimientos.

BBJ Tyrtulia
Estilo de API RPC: todo es POST /api/<endpoint> REST con hipermedia (HAL): solo se conoce GET /
Permisos El cliente pregunta (is_admin, edit_query) Enlaces que aparecen o desaparecen
Autenticación Hash SHA-256 sin sal de la contraseña en cada petición Sin contraseñas: socket Unix o reto firmado con SSH
Errores 6 códigos numéricos propios RFC 9457 con un catálogo de tipos
Paginación Todo de golpe Por cursor
Concurrencia La última edición gana, sin avisar ETag e If-Match
Formato del texto Lenguaje propio ([bold: ...], [red: ...]) Markdown estándar
Validación Un endpoint db_validate JSON Schema enlazado desde cada formulario
Configuración config.json TOML

Después, las funcionalidades que echaba de menos.

BBJ Tyrtulia
Responder a un mensaje concreto Citas >>N dentro del texto Respuestas enlazadas, con vista en árbol
Menciones No Sí
Notificaciones No (pendiente desde 2017) Bandeja con respuestas, menciones y avisos
Tiempo real No, hay que preguntar cada poco Server-Sent Events, con reanudación
Historial de ediciones No, editar sobrescribe Completo, con diferencias y revertir
Borrado Elimina la fila Lápida: la numeración se conserva
Búsqueda Cada cliente busca en lo que ha descargado Texto completo en el servidor, sin importar tildes ni mayúsculas
Feeds No Atom del foro y de cada hilo
Moderación Admin o no Roles, mensajes ocultos, reportes y registro público
Límite de peticiones No Por usuario y por IP

En común tienen un servidor pequeño sobre SQLite y varios clientes de texto contra una API documentada.

Clientes

En un foro headless sí o sí necesitas clientes.

Tyrtulia TUI

Tyrtulia TUI es el cliente para la terminal, hecho con Textual.

Un hilo en Tyrtulia TUI

tyrtulia.el

Si me conoces un poco, sabrás que este paquete era obligatorio: tyrtulia.el.

Un hilo en tyrtulia.el

Tyrtulia Web

Tyrtulia Web es más un espejo del contenido que un cliente. Su objetivo es indexar el foro para buscadores y permitir leerlo desde cualquier navegador.

Un hilo en Tyrtulia Web

Tiene un tema sin estilos y otro que imita mi web.

Federación

Puedes conectarte a un foro o configurar varios. La federación ocurre en los clientes: los servidores no se hablan entre sí ni copian mensajes de unos a otros. Cada foro es independiente, con sus usuarios, sus normas y sus moderadores.

En lugar de separarlos en pestañas o cuentas, los clientes mezclan los hilos de todos en una sola lista.

Tres foros en una misma lista

Eso sí, solo puedes escribir en los foros donde tengas cuenta. En el resto solo puedes leer.

Me parece una característica bastante chula, porque simplifica mucho el código de la API y te permite explorar otros foros con coste cero.

Pruébalo

Ha sido divertido diseñarlo e implementarlo. Por supuesto que hay foros más completos, sistemas de federación más potentes e incluso sistemas descentralizados que dejan Tyrtulia en pañales (Aether, Nostr...), pero su nicho es muy concreto: un foro ligero, de acceso cerrado, federado, indexable, seguro y con clientes de texto. Para todo lo demás, ya hay cosas mejores.

Si te animas a probarlo, tienes la guía de instalación en el repositorio de Tyrtulia.

Espero tu feedback.

Help me keep writing

Every coffee gives me a push toward the next article.

Comments

There are no comments yet.

You may also like