Архитектура Gateway
Последнее обновление: 2026-01-22
Обзор
- Один долгоживущий Gateway владеет всеми поверхностями обмена сообщениями (WhatsApp через Baileys, Telegram через grammY, Slack, Discord, Signal, iMessage, WebChat).
- Клиенты контрольной плоскости (приложение macOS, CLI, веб-интерфейс, автоматизации) подключаются к Gateway через WebSocket на настроенном хосте привязки (по умолчанию 127.0.0.1:18789).
- Узлы (macOS/iOS/Android/headless) также подключаются через WebSocket, но объявляют role: node с явными возможностями/командами.
- Один Gateway на хост; это единственное место, которое открывает сессию WhatsApp.
- canvas host (по умолчанию 18793) обслуживает редактируемый агентом HTML и A2UI.
Компоненты и потоки
Gateway (демон)
- Поддерживает соединения провайдеров.
- Предоставляет типизированный WS API (запросы, ответы, события server-push).
- Валидирует входящие фреймы по JSON Schema.
- Генерирует события типа agent, chat, presence, health, heartbeat, cron.
Клиенты (приложение mac / CLI / веб-админ)
- Одно WS-соединение на клиент.
- Отправляют запросы (health, status, send, agent, system-presence).
- Подписываются на события (tick, agent, presence, shutdown).
Узлы (macOS / iOS / Android / headless)
- Подключаются к тому же WS-серверу с role: node.
- Предоставляют идентификатор устройства в connect; сопряжение на основе устройства (роль node) и одобрение хранится в хранилище сопряжений устройств.
- Предоставляют команды типа canvas.*, camera.*, screen.record, location.get.
Детали протокола:
WebChat
- Статический UI, который использует WS API Gateway для истории чатов и отправок.
- В удаленных настройках подключается через тот же SSH/Tailscale туннель, что и другие клиенты.
Жизненный цикл соединения (один клиент)
Клиент Gateway
| |
|---- req:connect -------->|
|<------ res (ok) ---------| (или res error + close)
| (payload=hello-ok содержит снапшот: presence + health)
| |
|<------ event:presence ---|
|<------ event:tick -------|
| |
|------- req:agent ------->|
|<------ res:agent --------| (ack: \{runId,status:"accepted"\})
|<------ event:agent ------| (потоковая передача)
|<------ res:agent --------| (финал: \{runId,status,summary\})
| |
Протокол связи (краткое описание)
- Транспорт: WebSocket, текстовые фреймы с JSON-пейлоадами.
- Первый фрейм должен быть connect.
- После рукопожатия:
- Запросы: {type:"req", id, method, params} → {type:"res", id, ok, payload|error}
- События: {type:"event", event, payload, seq?, stateVersion?}
- Если установлен OPENCLAW_GATEWAY_TOKEN (или --token), connect.params.auth.token должен совпадать, иначе сокет закрывается.
- Ключи идемпотентности требуются для методов с побочными эффектами (send, agent) для безопасного повтора; сервер хранит краткосрочный дедупликационный кеш.
- Узлы должны включать role: "node" плюс возможности/команды/разрешения в connect.
Сопряжение + локальное доверие
- Все WS-клиенты (операторы + узлы) включают идентификатор устройства в connect.
- Новые ID устройств требуют одобрения сопряжения; Gateway выдает токен устройства для последующих подключений.
- Локальные подключения (loopback или собственный tailnet-адрес хоста gateway) могут быть автоматически одобрены для поддержания плавного UX на том же хосте.
- Не-локальные подключения должны подписать nonce connect.challenge и требуют явного одобрения.
- Аутентификация Gateway (gateway.auth.*) все равно применяется ко всем соединениям, локальным или удаленным.
Детали: Протокол Gateway, Сопряжение, Безопасность.
Типизация протокола и кодогенерация
- Схемы TypeBox определяют протокол.
- JSON Schema генерируется из этих схем.
- Swift-модели генерируются из JSON Schema.
Удаленный доступ
- Предпочтительно: Tailscale или VPN.
- Альтернатива: SSH-туннель
ssh -N -L 18789:127.0.0.1:18789 user@host - То же рукопожатие + токен аутентификации применяются через туннель.
- TLS + опциональное пиннинг можно включить для WS в удаленных настройках.
Снапшот операций
- Запуск: openclaw gateway (на переднем плане, логи в stdout).
- Проверка здоровья: health через WS (также включено в hello-ok).
- Супервизор: launchd/systemd для автоперезапуска.
Инварианты
- Ровно один Gateway контролирует одну Baileys-сессию на хост.
- Рукопожатие обязательно; любой не-JSON или не-connect первый фрейм — это жесткое закрытие.
- События не воспроизводятся; клиенты должны обновляться при пропусках.