Un chat WebSocket es un bus de mensajes: lo que se puede construir encima


14 de marzo de 2026

Esta semana montamos un chat en tiempo real para el blog. WebSocket, ReactPHP, un Web Component, drag and drop. Lo normal seria dejarlo ahi: ya funciona, la gente puede hablar, siguiente tarea.

Pero si te paras a mirar lo que tenemos, resulta que hemos construido algo mas gordo de lo que parece.

Lo que parece vs lo que es

Lo que parece: un chat en una esquina del blog.

Lo que es: un servidor WebSocket integrado en un event loop async de PHP que acepta conexiones de cualquier cliente que hable el protocolo, les asigna un UUID, y hace broadcast de JSON a todos los conectados.

El Chat.php del servidor no sabe que hay al otro lado de cada conexion. No le importa. Puede ser un navegador, un script de Python, un bot de Telegram, una IA, un sensor de temperatura. Si abre un WebSocket y manda texto, participa.

Eso no es un chat. Es un bus de mensajes en tiempo real.

Modelo local en el chat

Tenemos una maquina con dos Xeon E5-2699v3 y 128GB de RAM. Eso da para correr un modelo de lenguaje local con Ollama: Qwen, Mistral, Phi, lo que encaje en memoria.

La idea:

Ollama (modelo local)
    |
    v
Cliente WebSocket (script PHP/Python)
    |
    v
Chat WebSocket (:8001) --broadcast--> Navegadores

Un script que abre una conexion WebSocket como un usuario mas, escucha los mensajes, y cuando alguien le menciona (o siempre, segun se configure), le pasa el mensaje al modelo y devuelve la respuesta por el mismo WebSocket.

Sin API de terceros. Sin coste por token. Sin latencia de red. Todo local. El modelo responde en el chat como un participante mas, con su propio UUID, indistinguible de un humano (bueno, mas o menos).

Y no solo un modelo. Podrian ser varios. Un experto en PHP, otro en NixOS, otro que solo hace chistes malos. Cada uno es un cliente WebSocket con su propia personalidad conectada a un modelo o prompt diferente.

Bridge con Telegram

Telegram tiene una API de bots trivial de usar. Un bridge bidireccional seria:

Telegram (canal/grupo)
    ^
    | Bot API
    v
Bridge (script)
    ^
    | WebSocket
    v
Chat Cohete (:8001)

Mensaje en Telegram -> llega al bridge -> se envia por WebSocket -> aparece en el blog. Mensaje en el blog -> broadcast a todos -> el bridge lo recibe -> lo reenvia a Telegram.

Resultado: la gente del blog y la gente de Telegram estan en la misma conversacion. Sin saber que usan plataformas distintas.

Esto abre posibilidades interesantes. Un lector del blog que no quiere tener una pestana abierta puede seguir la conversacion desde el movil via Telegram. Un colaborador que esta en el metro puede participar en la discusion de un post sin abrir el navegador.

Notificaciones en tiempo real

Ahora mismo el blog es estatico en el sentido de que si alguien publica un post nuevo, no te enteras hasta que refrescas. Con el WebSocket ya montado, el servidor podria emitir eventos:

// Desde el servidor, al publicar un post:
{
    "type": "new_post",
    "title": "WebSockets con PHP",
    "author": "Ambrosio",
    "url": "/blog/author/Ambrosio/websockets-con-php"
}

// Desde el servidor, al recibir un comentario:
{
    "type": "new_comment",
    "postId": "7873f71d-...",
    "author": "Hassan"
}

El ChatBox.js (o un componente hermano) podria mostrar una notificacion tipo toast. O un badge en la burbuja minimizada. "3 mensajes nuevos". "Hassan ha comentado en tu post". En tiempo real, sin polling, sin recargar la pagina.

Salas y canales

Ahora mismo todo va a un broadcast global. Todos los conectados ven todos los mensajes. El siguiente paso logico son las salas:

La implementacion en el servidor es un Map<string, ConnectionPool>. Cada sala es un pool de conexiones. El cliente manda {type: "join", room: "post-7873f71d"} y el servidor lo anade al pool correspondiente. Los broadcasts van solo a la sala.

En el frontend, el atributo group del <chat-box> ya existe. Solo falta que el servidor lo respete.

Pair programming en vivo

Esta es la que mas me gusta. Imagina: estas escribiendo codigo, tienes el blog abierto con el chat, y alguien que esta leyendo tu post te hace una pregunta. Le respondes en tiempo real. Abres una sala, compartes un snippet, discutis la implementacion.

No es Slack. No es Discord. Es un chat que vive dentro del blog, junto al contenido tecnico. El contexto ya esta ahi: el post que estas leyendo es el tema de conversacion.

Y si ademas hay un modelo local escuchando, puede participar en la discusion. "Oye, ese patron que mencionas tiene un edge case con conexiones concurrentes, mira esto…" Un rubber duck que de verdad sabe de lo que habla.

Monitor de infraestructura

El WebSocket no tiene por que llevar solo texto humano. Podria llevar metricas:

// Un script que lee /proc y lo emite cada 5 segundos:
{
    "type": "metrics",
    "cpu": 23.4,
    "mem": 67.2,
    "connections": 12,
    "uptime": "5d 14h"
}

Un Web Component <server-monitor> que se suscribe al mismo WebSocket y renderiza graficas en tiempo real. Sin Grafana. Sin Prometheus. Un dashboard minimalista que vive en el blog, construido sobre la misma infraestructura del chat.

Es overengineering? Probablemente. Pero es nuestro overengineering y correria en 20 lineas de codigo.

Lo que no hay que hacer

Con todas estas posibilidades es facil caer en la trampa de querer montarlo todo a la vez. No.

Lo que tenemos ahora funciona. Un chat, un broadcast, un Web Component bonito. Cada extension se puede construir de forma incremental porque la base es solida:

No hay que reescribir nada. Hay que conectar cosas al bus.

La leccion

Un WebSocket server no es una feature. Es infraestructura. Como una base de datos o una cola de mensajes. Una vez que lo tienes corriendo, el coste marginal de cada nueva funcionalidad que lo usa es casi cero.

Montamos un chat. Resulta que montamos una plataforma.

Comparte este post:

Es tu post

Estas seguro? Esto no se puede deshacer.

Comentarios (0)

Sin comentarios todavia. Se el primero!

Deja un comentario