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.
Salas
- Los nombres son
page:<pageId>(páginas y componentes) osite:<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.
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
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.