Создавайте приложения для совместной работы в реальном времени с Hocuspocus 4 и Yjs CRDT. От настройки WebSocket-сервера до продакшен-развёртывания — практическое руководство с примерами кода для Node, Bun, Deno и Cloudflare Workers.
Совместная работа в реальном времени давно перестала быть опцией — это ожидание. Google Docs, Notion, Figma и Linear задали стандарт: несколько пользователей могут одновременно редактировать один документ, видеть курсоры друг друга и никогда не терять данные — даже при обрыве соединения.
Создать такое с нуля очень сложно. Нужно разрешение конфликтов, операционные преобразования, синхронизация состояния, информирование о присутствии и сохранение данных. Здесь на помощь приходят Yjs и Hocuspocus.
Yjs — это CRDT (Conflict-Free Replicated Data Type) библиотека, которая автоматически разрешает конфликты. Hocuspocus — это WebSocket-сервер, управляющий синхронизацией Yjs-документов между клиентами с поддержкой сохранения данных, аутентификации и масштабирования.
Hocuspocus 4 вышел в июне 2026 года и стал самым значительным обновлением — с поддержкой нескольких платформ (Node.js, Bun, Deno, Cloudflare Workers), переработанным API расширений, улучшенным сохранением данных и значительным приростом производительности. Давайте разберёмся подробно.
Прежде чем погружаться в Hocuspocus, важно понять технологию под ним. CRDT — это структуры данных, которые можно реплицировать между пользователями и автоматически объединять без конфликтов — без центральной координации.
В отличие от операционных преобразований (Operational Transformation, используется в Google Docs), у CRDT есть несколько ключевых преимуществ:
Yjs реализует алгоритм YATA — тип CRDT, оптимизированный для текстового редактирования. Он представляет документ как связный список элементов, каждый с уникальным идентификатором (client ID + clock). Когда два пользователя вставляют текст в одной позиции, Yjs упорядочивает вставки детерминированно — гарантируя, что все клиенты придут к одному состоянию.
// Алгоритм YATA в Yjs — упрощённая концепция
// Каждый элемент: { id: [clientId, clock], content, origin }
// Две вставки в позиции 5 от разных клиентов:
const itemA = { id: [1, 42], content: "Привет", origin: [null, 5] };
const itemB = { id: [2, 17], content: "Мир", origin: [null, 5] };
// Yjs упорядочивает по (clientId, clock) — itemB раньше itemA
// так как 2 < 1 создаёт детерминированный порядок
// Результат: "МирПривет" — одинаково на всех клиентах
Hocuspocus — это официальное серверное решение для Yjs. Думайте о нём как о «бэкенде для Yjs» — он управляет:
Без Hocuspocus вам пришлось бы строить всё это самостоятельно поверх «сырого» Yjs — возможно, но трудоёмко и чревато ошибками. Hocuspocus упаковывает это в чистый, настраиваемый сервер с архитектурой плагинов.
Hocuspocus 4, вышедший в начале июня 2026 года — это переписанная с нуля версия с несколькими крупными улучшениями:
Главная новость: Hocuspocus 4 работает на Node.js, Bun, Deno и Cloudflare Workers. Один и тот же API доступен на всех платформах с платформо-специфичными адаптерами для различий в файловом вводе-выводе, реализации WebSocket и бэкендах хранения.
Система расширений полностью обновлена. Вместо старых классовых хуков Hocuspocus 4 использует конвейер в стиле middleware, где расширения регистрируют обработчики для событий жизненного цикла:
beforeConnect — проверка запросов на подключение до их принятияonConnect — настройка состояния соединения (контекст пользователя, права)onLoadDocument — преобразование или гидратация документов при загрузкеonStoreDocument — перехват и изменение документов перед сохранениемonDisconnect — очистка при отключении клиентаСохранение в SQLite теперь использует WAL-режим по умолчанию, что значительно улучшает параллельную производительность чтения и записи. Новый адаптер PostgreSQL поддерживает пул соединений и подходит для многосерверных развёртываний. Адаптер Redis позволяет кэшировать документы в памяти с опциональным сохранением на диск.
Hocuspocus 4 предоставляет полноценный адаптер для Cloudflare Workers, использующий Durable Objects для поддержания WebSocket-соединений и DO storage для хранения документов. Это означает, что вы можете запустить инфраструктуру совместной работы целиком на периферии с глобальным низким временем отклика.
Новый протокол дельта-синхронизации снижает использование полосы пропускания на 40-60%. Управление памятью переписано для эффективной работы с документами, которые редактируют тысячи пользователей одновременно. Время запуска сервера примерно в 3 раза быстрее, чем в Hocuspocus 3.
Давайте создадим минимальный сервер Hocuspocus 4. Сначала установите пакет:
npm install @hocuspocus/server @hocuspocus/extension-sqlite
Теперь создайте базовый сервер:
import { Server } from '@hocuspocus/server'
import { SQLiteExtension } from '@hocuspocus/extension-sqlite'
const server = Server.configure({
port: 8080,
// Сохранение документов в SQLite
extensions: [
new SQLiteExtension({
database: 'collab.db',
// WAL-режим включён по умолчанию в Hocuspocus 4
}),
],
// Хук аутентификации
async beforeConnect({ connection }) {
const token = connection.token
if (!token || !validateToken(token)) {
throw new Error('Требуется аутентификация')
}
const userData = decodeToken(token)
return { user: userData }
},
})
server.listen().then(() => {
console.log('Сервер Hocuspocus 4 запущен на порту 8080')
})
Всё. Примерно 15 строк серверного кода дают вам:
На стороне клиента подключаемся к серверу Hocuspocus через пакет
@hocuspocus/provider:
npm install @hocuspocus/provider yjs
import * as Y from 'yjs'
import { HocuspocusProvider } from '@hocuspocus/provider'
// Создаём Yjs документ
const doc = new Y.Doc()
// Подключаемся к серверу Hocuspocus
const provider = new HocuspocusProvider({
url: 'ws://localhost:8080',
name: 'my-document-id', // уникальный идентификатор документа
doc,
token: 'your-jwt-token', // передаётся в хук beforeConnect
})
// Теперь документ синхронизирован с сервером
// Все изменения автоматически отправляются другим клиентам
// Пример: наблюдаем за изменениями
const array = doc.getArray('my-list')
array.observe(event => {
console.log('Документ изменён:', array.toArray())
})
// Делаем изменение — оно автоматически синхронизируется
array.push(['Привет от клиента A!'])
Провайдер автоматически обрабатывает переподключение, синхронизацию состояния и разрешение конфликтов. Вы можете подписаться на события статуса соединения:
provider.on('status', (event) => {
switch (event.status) {
case 'connected':
console.log('Подключено к Hocuspocus')
break
case 'disconnected':
console.log('Отключено — переподключаюсь...')
break
case 'connecting':
console.log('Попытка подключения...')
break
}
})
Самый частый сценарий использования Yjs — совместное редактирование форматированного текста. Связка y-prosemirror соединяет Yjs-документы с ProseMirror:
npm install y-prosemirror prosemirror-state prosemirror-view prosemirror-model prosemirror-schema-basic prosemirror-example-setup
import { EditorState } from 'prosemirror-state'
import { EditorView } from 'prosemirror-view'
import { schema } from 'prosemirror-schema-basic'
import { exampleSetup } from 'prosemirror-example-setup'
import { yDocPlugin, yCursorPlugin, yUndoPlugin } from 'y-prosemirror'
import * as Y from 'yjs'
import { HocuspocusProvider } from '@hocuspocus/provider'
const doc = new Y.Doc()
const provider = new HocuspocusProvider({
url: 'ws://localhost:8080',
name: 'document-id',
doc,
})
// Получаем Yjs-тип для ProseMirror
const yDocType = doc.get('prosemirror', Y.XmlFragment)
// Создаём редактор ProseMirror, подключённый к Yjs
const state = EditorState.create({
schema,
plugins: [
yDocPlugin(yDocType), // двусторонняя синхронизация с Yjs
yCursorPlugin(provider.awareness!), // отображение курсоров других пользователей
yUndoPlugin(), // общая история отмены
...exampleSetup({ schema }),
],
})
const view = new EditorView(document.querySelector('#editor'), { state })
// Теперь каждое нажатие клавиши синхронизируется в реальном времени
// Другие пользователи видят ваш курсор и правки мгновенно
Плагин yCursorPlugin предоставляет совместные курсоры и выделение
«из коробки» — пользователи видят позиции курсоров, выделения, имена, цвета и
аватары друг друга.
Hocuspocus 4 поддерживает четыре бэкенда сохранения данных. Выбор зависит от модели развёртывания и масштаба требований:
| Бэкенд | Для чего лучше | Масштабирование | Производительность |
|---|---|---|---|
| SQLite | Один сервер, прототипирование, небольшие команды | Один экземпляр | Быстро с WAL; ~10K документов на скромном железе |
| PostgreSQL | Многосерверные продакшен-развёртывания | Горизонтальное через пул соединений | Хорошо; пропускная способность ограничена блокировками строк |
| Redis | Высокая пропускная способность, эфемерные документы | Кластерный режим | Отлично; скорость in-memory |
| Cloudflare DO | Edge-развёртывания, глобальная аудитория | Автоматическое (Durable Objects) | Очень хорошо; данные рядом с пользователями |
import { Server } from '@hocuspocus/server'
import { PostgreSQL } from '@hocuspocus/extension-postgresql'
const server = Server.configure({
port: 8080,
extensions: [
new PostgreSQL({
connection: {
host: 'localhost',
port: 5432,
database: 'collab',
user: 'app',
password: process.env.DB_PASSWORD,
},
// Автомиграция схемы при первом запуске
migrate: true,
// Пул из 10 соединений
poolSize: 10,
}),
],
})
server.listen()
Аутентификация в Hocuspocus 4 работает через конвейер расширений. Хук
beforeConnect срабатывает до установки WebSocket-соединения,
позволяя проверить учётные данные и отклонить неавторизованные подключения:
import { Server } from '@hocuspocus/server'
import jwt from 'jsonwebtoken'
const server = Server.configure({
// ...другие настройки...
async beforeConnect({ connection }) {
// Извлекаем JWT из токена соединения
const { token } = connection
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET)
// Возвращаем контекст пользователя — доступен во всех последующих хуках
return {
user: {
id: decoded.sub,
name: decoded.name,
role: decoded.role,
},
}
} catch (error) {
// Отклоняем соединение
throw new Error('Невалидный или просроченный токен')
}
},
// Авторизация на уровне документа
async onLoadDocument({ documentName, user }) {
// Проверяем, есть ли у пользователя доступ к этому документу
const hasAccess = await checkDocumentAccess(user.id, documentName)
if (!hasAccess) {
throw new Error('Доступ к этому документу запрещён')
}
},
})
Поддержка Cloudflare Workers в Hocuspocus 4 — одна из самых интересных возможностей. Вы можете развернуть весь бэкенд совместной работы на периферии с хранением документов в Durable Objects:
import { Server } from '@hocuspocus/server'
import { CloudflareDO } from '@hocuspocus/extension-cloudflare'
// Это Durable Object, работающий на периферии Cloudflare
export class HocuspocusDO {
constructor(state, env) {
this.server = Server.configure({
extensions: [
new CloudflareDO({
state,
// DO storage для сохранения данных
storage: state.storage,
}),
],
})
}
async fetch(request) {
return await this.server.fetch(request)
}
}
// wrangler.toml
// [durable_objects]
// bindings = [{ name = "HOCUSPOCUS", class_name = "HocuspocusDO" }]
Когда ваше приложение вырастает за пределы одного сервера, Hocuspocus 4 поддерживает горизонтальное масштабирование через Redis pub/sub:
import { Server } from '@hocuspocus/server'
import { RedisExtension } from '@hocuspocus/extension-redis'
const server = Server.configure({
port: 8080,
extensions: [
new RedisExtension({
// Redis pub/sub для кросс-серверной синхронизации
publisher: { host: 'redis-cluster', port: 6379 },
subscriber: { host: 'redis-cluster', port: 6379 },
}),
// Сохранение данных на каждом сервере
new PostgreSQL({ /* ... */ }),
],
})
// Развёртывание за балансировщиком нагрузки
// Все экземпляры обмениваются состоянием документов через Redis pub/sub
// Каждый экземпляр независимо сохраняет данные в PostgreSQL
Помимо синхронизации документов, приложениям совместной работы нужна
осведомлённость — знание о том, кто онлайн, где находится
курсор и что делает пользователь. Yjs предоставляет протокол
Awareness, а Hocuspocus ретранслирует его между клиентами:
// Клиент: обновление состояния осведомлённости
provider.awareness.setLocalState({
user: {
name: 'Алиса',
color: '#ff6b6b',
avatar: '/avatars/alice.png',
},
cursor: { x: 450, y: 120 },
selection: { from: 23, to: 45 },
})
// Клиент: отслеживание других пользователей
provider.awareness.on('change', ({ states }) => {
states.forEach((state, clientId) => {
if (clientId !== doc.clientID) {
// Отображаем курсоры и выделения других пользователей
renderRemoteCursor(state.user, state.cursor)
}
})
})
| Характеристика | Hocuspocus 4 | Liveblocks | PartyKit | ShareDB |
|---|---|---|---|---|
| Технология | Yjs CRDT | Проприетарный CRDT | Yjs CRDT | OT (JSON0) |
| Свой сервер | Да | Нет (SaaS) | Частично | Да |
| Платформы | Node, Bun, Deno, CF Workers | Node (через SDK) | PartyKit Cloud | Node.js |
| Сохранение данных | SQLite, PostgreSQL, Redis, DO | Управляемое | SQLite, DO | MongoDB, PostgreSQL |
| Офлайн-режим | Встроенный (CRDT) | Ограниченный | Встроенный (CRDT) | Нет |
| Rich text | ProseMirror, Quill, TipTap | Встроенный редактор | ProseMirror | Quill (через плагин) |
| Цена | Бесплатно (MIT) | От $599/мес | От $49/мес | Бесплатно (MIT) |
Классический сценарий. Несколько пользователей редактируют один документ, видят курсоры друг друга и никогда не теряют работу. Компании используют это для внутренних вики, написания предложений, документирования кода и протоколов совещаний. С TipTap или ProseMirror в качестве редактора вы получаете полноценный rich text.
Yjs не ограничивается текстом. С помощью y-indexeddb для офлайн-кэширования и кастомных Yjs-типов для фигур, соединителей и слоёв команды могут совместно работать над диаграммами, вайрфреймами и досками — как в Figma или Excalidraw.
Используя y-monaco (связка Yjs с Monaco Editor) или y-codemirror.next, можно добавить совместное редактирование кода. Команды используют это для парного программирования, ревью кода и учебных занятий.
Нужна разработка с совместной работой? Мои услуги
Hocuspocus 4 делает совместную работу в реальном времени доступной для любого проекта. Создаёте ли вы следующий Google Docs, совместную доску, инструмент для парного программирования или систему управления проектами — комбинация Yjs CRDT и Hocuspocus даёт вам продакшен-готовый фундамент.
Лучшая часть? Это бесплатно, open-source и размещается на вашем сервере. Вы владеете своими данными и инфраструктурой. Никакой лицензии на рабочее место, никакой привязки к вендору, никакой передачи данных на чужие серверы.
Если вы планируете проект, которому нужна совместная работа в реальном времени, и хотите, чтобы опытный разработчик спроектировал и реализовал его — свяжитесь со мной. Я создавал коллаборативные приложения с Yjs, Hocuspocus, WebSocket и различными фронтенд-фреймворками и помогу вам выбрать правильную архитектуру и избежать типичных ошибок.
Нужна совместная работа в реальном времени для вашего приложения? Расскажите о проекте — бесплатная первичная консультация.