MallaDocs

Tiempo real y multijugador

Salas Yjs en Durable Objects, presencia, puntos de control y actualizaciones de agentes que se pueden deshacer.

Guía para desarrolladores

Cada página, componente y sitio tiene una sala: un Durable Object YRoom que sincroniza un documento Yjs por WebSockets, mantiene la presencia, persiste en su propio almacenamiento SQLite y guarda checkpoints en Postgres. La sala también es la ruta de guardado del editor, incluso cuando editas solo.

TokenGET /auth/realtime-token?siteId= de la api firma un token de corta duración.
ConexiónWebSocket a /rooms/page:<id>?token=… ; se comprueban el origen y el sitio.
SincronizaciónProtocolo y-websocket; cada actualización se valida antes de guardarse.
CheckpointUna alarma escribe el JSON + el estado Yjs en Postgres en menos de 30 s.

Salas

  • Los nombres son page:<pageId> (páginas y componentes) o site:<siteId> (lista de páginas, clases, tokens, variables, fórmulas).
  • Los tokens se comprueban por sala; los lectores reciben el documento y la presencia, pero sus actualizaciones se descartan. Los editores de contenido solo pueden cambiar texto.
  • Los sockets hibernan, así que las salas inactivas no cuestan nada; la presencia sobrevive al despertar gracias a los attachments de los sockets.
  • Límites: 4 MiB por mensaje, 256 sockets por sala.
ts
import { WebsocketProvider } from 'y-websocket'

const provider = new WebsocketProvider(realtimeUrl + '/rooms', 'page:p_home', ydoc, {
  params: { token },
})

Validación en el servidor

Cada actualización se aplica primero a una copia en la sombra. Cada registro que tocó se vuelve a leer como JSON y se comprueba: esquema del nodo, enlaces de datos solo en props enlazables, $model solo en campos de formulario, que cada $expr se pueda parsear, y la forma de los flujos de trabajo. Una actualización que falla ni se guarda ni se retransmite, y el emisor se cierra con 4422, un código permanente para que el cliente no la reenvíe.

Protocolo

text
type 0    sync          step1 / step2 / update (relayed to other sockets)
type 1    awareness     cursors, selections (relayed to every socket)
type 3    query awareness
type 100  agent update  Malla extension: origin byte (0 = ai, 1 = mcp) + Yjs update,
                        sent only to the sockets of the user an AI/MCP edit acted for

Persistencia

  • Un registro de actualizaciones de solo anexado más un snapshot, compactado a las 500 filas o 1 MB y cuando se va el último socket.
  • Las salas vacías se rellenan desde Postgres antes de la primera sincronización, así que un cliente nunca sincroniza un documento vacío encima de contenido real.
  • Los checkpoints en Postgres se reintentan con backoff durante un máximo de cinco minutos; la sincronización nunca los espera.

Ediciones de IA y MCP que se pueden deshacer

Las ediciones del lado del servidor llegan a la sala mediante POST /internal/rooms/:room/ops, con el usuario en cuyo nombre se actúa. La sala las aplica una vez, envía a los sockets de ese usuario una agent update (tipo 100) y a todos los demás una actualización normal. El editor aplica las agent updates con el origen ai o mcp, que su gestor de deshacer registra como un paso aparte. Así, ese usuario puede deshacer el cambio del agente, y nadie puede deshacer el de otra persona.