Entrixy v2 — итоговая архитектура

Согласовано 13 апреля 2026. Рабочий документ для реализации.

Содержание
  1. Приватность: сервер не знает ничего
  2. Четыре типа объектов: звонок, URL, устройство (WS), BLE-контроллер
  3. Webhook (URL) подробно
  4. Устройство (ESP32 / ESP8266, WebSocket) подробно
  5. BLE-контроллер (ESP32) подробно
  6. Триггеры: GPS и Wi-Fi
  7. Три уровня подтверждения
  8. Владелец против гостя — разделение настроек
  9. Интерфейс приложения
  10. Лимиты тарифа
  11. Иконки объектов для гостей
  12. Прогноз нагрузки
  13. Структура базы данных
  14. План реализации

1. Приватность: сервер не знает ничего (по умолчанию)

Обещание, которое мы даём: Мы не знаем ни телефонов наших пользователей, ни их координат

Все чувствительные данные хранятся только на телефоне. Часть из них владелец по желанию может пропустить через сервер — с явным предупреждением.

ДанныеПо умолчаниюПо желанию через сервер
Координаты (lat, lon)Только на телефонеМожно поделиться с гостями (с предупреждением)
Отпечаток Wi-Fi (BSSID[])Только на телефонеНе передаётся никогда
Адрес и секрет webhookТолько на телефонеМожно хранить на сервере (с предупреждением)
Радиус, время, уровень подтвержденияЧерез сервер
Отпечаток Wi-Fi равносилен координатам. По набору BSSID сервисы определяют положение с точностью 20–50 м. Картина Wi-Fi не проходит через сервер ни при каких условиях.

2. Четыре типа объектов

При добавлении объекта владелец выбирает тип связи. Дальше открывается окно настроек этого типа.

IconНазваниеКак это работаетFeedbackExample
📞CallТелефонный звонок с телефона владельцаНет (звонок либо ушёл, либо нет)Шлагбаум, домофон
🌐URL (webhook)HTTP-запрос на адрес устройства — с телефона или с сервера EntrixyДа — JSON-ответ со статусомУмный замок с API, Tasmota, Shelly Cloud
📟Интернет-контроллер (WS)Команда по WebSocket подключённому контроллеруДа — ответ по WebSocketСамодельные ESP32 / ESP8266, Shelly Plus 1, NodeMCU
📶BLE-контроллерПрямой канал Bluetooth между телефоном и контроллером, без интернетаДа — ответ по BLE (notify)Контроллер на батарейках у ворот без Wi-Fi

Конфигуратор прошивки для двух последних типов: /controller/ (BLE и Интернет, в браузере).

Для пользователя все типы выглядят одинаково: карточка, кнопка Позвонить, статус. Отличается только механизм доставки.

3. Webhook (URL) подробно

Два режима отправки

В окне настроек webhook есть переключатель, а плюсы и минусы расписаны прямо на экране:

◉ С телефона владельца (по умолчанию)
Запрос уходит с вашего телефона. Адрес и секрет устройства остаются у вас — сервер Entrixy их не узнаёт.
Плюс: максимум приватности
Минус: телефон должен быть в сети
○ С сервера Entrixy
Запрос отправляет наш сервер. Работает, даже когда телефон офлайн.
Плюс: работает без телефона владельца
Минус: адрес устройства хранится на сервере

Где живут данные

ModeГде адрес и секретЗнает ли сервер адрес?
С телефонаPrefs на телефоне владельцаNo
С сервераТаблица numbers в базе данныхYes

Протокол запроса

Безопасность: подпись HMAC. У устройства и Entrixy общий webhook_secret.

POST https://device-url.com/open
Content-Type: application/json

{
  "action": "open",
  "object_id": 42,
  "timestamp": 1713020000,
  "nonce": "a1b2c3",
  "signature": "hmac-sha256(secret, timestamp + nonce + action)"
}

Устройство проверяет, что подпись верна, отметка времени моложе 30 секунд, а nonce не повторный. Если всё в порядке — срабатывает и отвечает:

{"status": "ok", "message": "Door opened"}
or
{"status": "error", "message": "Lock jammed"}

Статус показывается пользователю в приложении.

Поток данных (режим телефона)

The guest presses Call
  → the server receives the request
  → sends it to the owner over the WebSocket: {type: "do_webhook", ...}
  → the owner's phone makes the HTTP call to the device
  → receives the JSON response
  → sends it to the server: {type: "webhook_result", status, message}
  → the server passes it to the guest
  → both see the status in the log

Logging

В обоих режимах журнал на телефоне владельца и гостя записывает, кто, когда, какой объект и с каким статусом.

4. Устройство (ESP32 / ESP8266, WebSocket) подробно

Как это работает

У контроллера — поддерживаются ESP32, ESP32-S3, ESP32-C3 и ESP8266 — нет публичного IP. Он держит исходящее соединение WebSocket с сервером Entrixy, ровно как телефон владельца.

Прошивка и конфигуратор живут по адресу /esp/socket/. Исходники: ESP32, ESP8266.

Протокол WebSocket устройства

// Device → server: connect
{"type": "device_hello", "device_key": "xyz789", "device_secret": "..."}

// Server → device: OK
{"type": "device_ok"}

// Server → device: command
{"type": "device_command", "action": "open", "command_id": "abc123"}

// Device → server: result
{"type": "device_response", "command_id": "abc123", "status": "ok", "message": "Opened"}

Сервер передаёт статус клиентам — владельцу и гостю — по их соединениям WebSocket.

Регистрация устройства

  1. В приложении владелец выбирает Добавить объект → тип Устройство
  2. Создаются device_key + device_secret
  3. Показывается QR-код с параметрами прошивки
  4. Пользователь прошивает ESP32 или ESP8266 (см. configurator), и устройство подключается к wss://entrixy.com/ws

Indication

На карточке объекта виден статус связи устройства — онлайн или офлайн, — так же как гости видят статус владельца.

4a. BLE-контроллер (ESP32) подробно

Как это работает

Контроллеру на ESP32, S3, C3, C6 или H2 для срабатывания не нужны ни Wi-Fi, ни сервер. Он лежит в глубоком сне, раз в секунду просыпается примерно на 200 мс и передаёт BLE-объявление с подписанным счётчиком. Когда рядом оказывается телефон с действующим ключом, Android — или приложение, если оно на переднем плане — ловит объявление, подключается по GATT, шлёт команду fire с одноразовым nonce, и контроллер замыкает реле коротким импульсом. Для бистабильных приводов и замков есть режим с раздельными открыть и закрыть и отслеживанием положения, где открыто/закрыто едет зашифрованным байтом в объявлении.

Сервер Entrixy в этом канале не участвует. Он нужен только при создании гостевого ключа: владелец подписывает GuestToken и передаёт его гостю по сквозно зашифрованному каналу. Дальше гость работает офлайн.

Протокол BLE вкратце

Полная спецификация протокола, побитовая раскладка и тест-векторы — по адресу entrixy.com/ble. Модель угроз — в отдельном документе.

Когда BLE лучше WebSocket

Когда WebSocket лучше BLE

На один привод можно поставить и BLE, и WebSocket — в приложении они будут двумя отдельными объектами. BLE — открывать вблизи без интернета, WebSocket — когда вы далеко.

Регистрация устройства

  1. В приложении владелец выбирает Добавить объект → тип BLE-контроллер
  2. Приложение сканирует эфир на устройства в режиме сопряжения (с именем entrixy-pair)
  3. Выбор устройства запускает обмен ECDH по GATT; общий секрет сохраняется в Prefs телефона и в NVS контроллера
  4. Ни QR-кода, ни нажатий кнопок — новое устройство само входит в режим сопряжения после загрузки программы

Прошивка и конфигуратор живут по адресу /esp/ble/. Исходник: /esp32-example/.

Indication

На карточке объекта: серый — в эфире не виден, далеко или выключен; оранжевый — виден, но автоматика не сработала; зелёный — сработало или срабатывает; серый с зелёной обводкой — недавно сработало, можно нажать и открыть снова.

Database

CREATE TABLE devices (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  host_id INT UNSIGNED NOT NULL,
  device_key VARCHAR(64) NOT NULL UNIQUE,
  secret_hash VARCHAR(64) NOT NULL,
  label VARCHAR(128) DEFAULT '',
  last_seen DATETIME NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

In server.php добавляется третья роль WebSocket: device (рядом с host и guest).

5. Триггеры: GPS и Wi-Fi

GPS и Wi-Fi работают независимо. Если настроены оба, стреляет то условие, которое выполнилось первым. Общая пауза между срабатываниями — 20 секунд.

GPS — как сейчас

Wi-Fi — два режима

Режим 1: картина Wi-Fi (отпечаток)

Режим 2: подключение к сети

Расход энергии

Хранение (Prefs, телефон не покидает)

wifi_trigger_<actionKey> = {
  "mode": "fingerprint" | "connect",
  "bssids": ["AA:BB:CC:DD:EE:FF", ...],
  "min_match": 5,
  "connect_bssid": "AA:BB:CC:DD:EE:FF",
  "connect_ssid": "TP-Link_5G",
  "enabled": true
}

6. Три уровня подтверждения

Владелец решает, требуется ли подтверждение. Как именно подтверждать личность, решает клиент — из того, что предлагает устройство.

LevelЧто видит пользовательExample
AutomaticНичего — объект открывается самШлагбаумы, ворота
ConfirmУведомление в шторке: Открыть [объект]? → одно нажатиеГараж, калитка, защита от ложных срабатываний
Подтвердить личностьБиометрия, PIN-код телефона или PIN-код приложенияВходная дверь

Подтвердить — не лишний шаг. Уведомление само появляется в шторке в нужный момент. Не надо искать приложение, разблокировать телефон и находить объект. Одно нажатие — и дверь открыта.

Приоритет способов подтверждения личности

  1. Биометрия, если есть датчик → BiometricPrompt
  2. PIN-код или пароль телефона, когда биометрии нет → KeyguardManager
  3. Собственный PIN-код приложения на устройстве без блокировки → наш собственный диалог

Временное окно — дополнительный фактор

Владелец задаёт Разрешённые часы: 07:00–23:00. Вне окна условие молча игнорируется. Ночной интервал тоже работает (22:00–06:00).

Три независимых фактора защиты

  1. WHERE — GPS, отпечаток Wi-Fi или подключение
  2. WHO — автоматически, подтверждение или биометрия
  3. WHEN — временное окно
Входная дверь: отпечаток Wi-Fi плюс подтверждение личности плюс 07:00–23:00 — крепче большинства умных замков.

7. Владелец против гостя — разделение настроек

SettingВладелец (своё)Владелец → гостямГость
Тип объектаChoosesПередаётReceives
CoordinatesЗадаётПо желанию (с предупреждением)Задаёт или получает
Радиус GPSЗадаётПередаётReceives
Отпечаток Wi-FiScansNeverСканирует сам
Минимум совпадений Wi-FiЗадаётПередаётReceives
Временное окноЗадаётПередаётReceives
ПодтверждениеЗадаётПередаётReceives
GPS вкл/выклyesyes
Wi-Fi вкл/выклyesyes

8. Интерфейс приложения

Добавление объекта (SettingsScreen)

Первый шаг — выбор типа связи:

┌────────────────────────────┐
│  Connection type           │
│                            │
│  📞  Call                  │
│  Opening with a phone      │
│  call                      │
│                            │
│  🌐  URL                   │
│  Sending a command to      │
│  a device address          │
│                            │
│  📟  Device                │
│  Connected through Entrixy │
│  (ESP32, Arduino)          │
└────────────────────────────┘

После выбора открывается окно настроек этого типа.

Развёрнутая карточка объекта (ActionCard)

┌──────────────────────────────────────────┐
│  Corner barrier             📶 3/5  312m │
│  Mine • ●                                │
├──────────────────────────────────────────┤
│  [Edit]  [Icon]                          │
│  ─────────────────────────               │
│  [📍 Geo]   [📶 Wi-Fi]                   │
└──────────────────────────────────────────┘

Окно 📍 Гео

Окно 📶 Wi-Fi

◉ По картине Wi-Fi

○ По подключению к сети

Ниже: временное окно и подтверждение — общие с Гео

Окно webhook (🌐 URL)

◉ С телефона (по умолчанию)
Адрес хранится только на вашем телефоне. Сервер Entrixy его не узнаёт.
Плюс: максимум приватности
Минус: телефон должен быть в сети
○ С сервера Entrixy
Запрос отправляет сервер. Работает без вашего телефона.
Плюс: работает всегда
Минус: адрес устройства хранится на сервере

Индикация на свёрнутой карточке

TriggerIndicator
GPS"312 m"
Отпечаток Wi-Fi📶 деления (0–4 по совпадениям)
Подключение Wi-Fi📶 зелёный или серый
Webhook/Device● зелёный (онлайн) или серый (офлайн)

9. Лимиты тарифа

Внутренние тарифы. Пользователю не показываются, но лимиты действуют.

Тариф по умолчаниюLimit
Объекты (numbers)10
Гости (user_keys)10

При превышении — мягкое сообщение: Число объектов ограничено. Обратитесь в поддержку, чтобы его увеличить.

CREATE TABLE plans (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(64) NOT NULL,
  max_numbers INT NOT NULL DEFAULT 10,
  max_keys INT NOT NULL DEFAULT 10
);
INSERT INTO plans (name) VALUES ('default');

ALTER TABLE hosts ADD COLUMN plan_id INT UNSIGNED DEFAULT 1;

10. База данных — полный список изменений

-- Plans
CREATE TABLE plans (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(64) NOT NULL,
  max_numbers INT NOT NULL DEFAULT 10,
  max_keys INT NOT NULL DEFAULT 10
);
INSERT INTO plans (name) VALUES ('default');

ALTER TABLE hosts ADD COLUMN plan_id INT UNSIGNED DEFAULT 1;

-- Object type and settings
ALTER TABLE numbers
  ADD COLUMN type ENUM('call','webhook','device') DEFAULT 'call',
  ADD COLUMN radius INT DEFAULT 80,
  ADD COLUMN time_from TIME NULL,
  ADD COLUMN time_to TIME NULL,
  ADD COLUMN security_level ENUM('auto','confirm','identity') DEFAULT 'auto',
  ADD COLUMN geo_available TINYINT(1) DEFAULT 1,
  ADD COLUMN wifi_available TINYINT(1) DEFAULT 1,
  ADD COLUMN wifi_min_match INT DEFAULT 5,
  ADD COLUMN share_lat DOUBLE NULL,
  ADD COLUMN share_lon DOUBLE NULL,
  ADD COLUMN webhook_url VARCHAR(512) NULL,
  ADD COLUMN webhook_secret VARCHAR(64) NULL,
  ADD COLUMN webhook_mode ENUM('phone','server') DEFAULT 'phone',
  ADD COLUMN device_id INT UNSIGNED NULL;

-- Devices (ESP32)
CREATE TABLE devices (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  host_id INT UNSIGNED NOT NULL,
  device_key VARCHAR(64) NOT NULL UNIQUE,
  secret_hash VARCHAR(64) NOT NULL,
  label VARCHAR(128) DEFAULT '',
  last_seen DATETIME NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

share_lat / share_lon — заполняются, только если владелец включил Делиться координатами с гостями.

webhook_url / webhook_secret — заполняются только в серверном режиме. В режиме телефона живут в Prefs.

11. Иконки объектов для гостей

Владелец ставит объекту свою иконку, и гость должен её видеть. Но иконка на сервере не хранится — сервер работает почтальоном: доставил и забыл.

Подготовка на стороне владельца

При установке иконка сразу ужимается до 64×64 PNG (около 3–5 КБ) и хранится локально в Prefs или файлом.

Как передаётся

1. The guest connects
   → guest_ok carries has_avatar: true for objects that have an icon

2. The guest client checks: has_avatar=true but no icon locally?
   → Yes → it asks the server

3. The server has no icon
   → it asks the owner over the WebSocket: {type: "avatar_request", number_id: 42}

4. The owner's phone
   → reads the local file
   → sends: {type: "avatar_data", number_id: 42, data: "base64..."}

5. The server passes it to the guest
   → {type: "avatar_data", number_id: 42, data: "base64..."}

6. The guest stores it locally and never asks again

7. The server stored nothing — the data merely passed through

Краевые случаи

SituationBehaviour
Владелец офлайнГость видит иконку по умолчанию, пока владелец не появится в сети.
50 гостей спрашивают разом50 × 5 КБ = 250 КБ по WebSocket — пустяк.
Владелец меняет иконкуhas_avatar_hash меняется, и гости запрашивают её заново
Владелец удаляет иконкуhas_avatar: false, и гости показывают иконку по умолчанию
Приватность сохраняется. Иконка шлагбаума — не чувствительные данные, но и она на сервере не оседает: проходит по WebSocket транзитом и на диск не пишется.

12. Прогноз нагрузки

Один активный владелец даёт

Один активный гость

Scaling

SubscribersОдновременно в сети (~15%)Соединений WebSocketServer
1 000~150~150Easy
10 000~1 500~1 500Easy
50 000~7 500~7 500один воркер на пределе
100 000~15 000~15 0002–3 воркера

Workerman на одном воркере: 5000–10 000 одновременных соединений, примерно по 20 КБ каждое.

Webhook и устройство

Узкое место

— MySQL, а не WebSocket. Но 250 000 вставок в сутки — 50 тысяч подписчиков × 5 вызовов — для MySQL на SSD пустяк.

Conclusion: до 50 000 абонентов текущий сервер справится без изменений. После — увеличиваем worker->и добавить индексы.

13. План реализации

  1. Database — тарифы, новые колонки в numbers и hosts, таблица devices
  2. Лимиты на сервере — проверки в number_add и key_create
  3. Тип объекта — выбор типа при добавлении, разные диалоги
  4. API — host_sync и guest_ok с новыми полями профиля
  5. Веб-хук — протокол HMAC, два режима отправки, статус ответа
  6. WS устройства — device_hello, device_command и device_response в server.php
  7. Триггер Wi-Fi — отпечаток и подключение
  8. Временное окно — time_from/time_to
  9. Подтверждение — три уровня: автоматически, подтвердить, подтвердить личность
  10. Icons — транзитная доставка по WebSocket, сжатие до 64×64
  11. UI — кнопки Гео и Wi-Fi, диалоги, индикаторы, выбор типа
  12. Передача координат — опция с предупреждением
  13. Сборка и выпуск
PWA: Триггер Wi-Fi недоступен — у браузеров нет Wi-Fi API. Webhook и Устройство работают через PWA: кнопка → сервер → устройство. Временное окно и подтверждение есть только в родном приложении.
Обратная совместимость: старых клиентов почти нет. Делаем как удобно, а при необходимости принудительно обновляем через version_check.

Entrixy — итоговая архитектура v2, согласовано 13 апреля 2026