Entrixy v2 — Финальная архитектура

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

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

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

Рекламное обещание: «Мы не знаем ни телефонов, ни координат пользователей»

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

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

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

При добавлении объекта хозяин выбирает тип подключения. Далее открывается модалка с настройками под этот тип.

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

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

Для пользователя все типы выглядят одинаково: карточка, кнопка «Вызов», статус. Разница только в механизме доставки команды.

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

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

В модалке настройки webhook — radio buttons с пояснением плюсов и минусов прямо на экране:

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

Хранение данных

РежимГде URL и секретСервер знает URL?
С телефонаPrefs на телефоне хозяина❌ Нет
С сервераТаблица numbers в БД✅ Да

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

Безопасность: 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)"
}

Устройство проверяет: signature верна, timestamp не старше 30 секунд, nonce не повторяется. Если ок — выполняет и отвечает:

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

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

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

Гость нажал «Вызов»
  → сервер получает запрос
  → шлёт хосту через WS: {type: "do_webhook", ...}
  → телефон хозяина делает HTTP на устройство
  → получает JSON-ответ
  → шлёт на сервер: {type: "webhook_result", status, message}
  → сервер пробрасывает гостю
  → оба видят статус в журнале

Журналирование

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

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

Как работает

Контроллер (ESP32, ESP32-S3, ESP32-C3, ESP8266 — поддерживаются все) не имеет белого IP. Держит исходящее WebSocket-соединение к серверу Entrixy — так же как телефон хоста.

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

WS-протокол устройства

// Устройство → сервер: подключение
{"type": "device_hello", "device_key": "xyz789", "device_secret": "..."}

// Сервер → устройство: ОК
{"type": "device_ok"}

// Сервер → устройство: команда
{"type": "device_command", "action": "open", "command_id": "abc123"}

// Устройство → сервер: результат
{"type": "device_response", "command_id": "abc123", "status": "ok", "message": "Opened"}

Сервер пробрасывает статус клиенту (хосту и гостю) через их WS-соединения.

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

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

Индикация

В карточке объекта: статус подключения устройства (онлайн / оффлайн), аналогично статусу хоста для гостей.

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

Как работает

Контроллер на ESP32 / S3 / C3 / C6 / H2 не использует WiFi и сервер для срабатывания. Спит в deep-sleep, раз в секунду просыпается на ~200 мс и шлёт BLE-advertise с подписанным счётчиком. Когда телефон с действующим ключом оказывается рядом — система Android (или приложение в активном состоянии) ловит advertise, подключается к контроллеру по GATT, шлёт fire-команду с одноразовым nonce, контроллер замыкает реле на короткий импульс.

Сервер Entrixy в этом канале не участвует. Он только нужен в момент создания гостевого ключа (хозяин подписывает GuestToken, передаёт гостю через E2EE-канал). После этого гость работает оффлайн.

BLE-протокол кратко

Полная битовая спецификация и test vectors — на /ble.

Когда BLE предпочтительнее WS

Когда WS предпочтительнее BLE

Можно ставить и BLE и WS на один и тот же привод — это будут два разных объекта в приложении. BLE для близкого срабатывания без интернета, WS для удалённых ситуаций.

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

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

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

Индикация

В карточке объекта: серый = не виден в эфире (далеко или отключён), оранжевый = виден но не сработала автоматика, зелёный = сработал/срабатывает, серый с зелёной обводкой = недавно сработал, можно тапнуть для повторного открытия.

БД

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
);

В server.php добавляется третья роль WS-соединения: device (рядом с host и guest).

5. Триггеры: GPS и WiFi

GPS и WiFi работают независимо. Если настроены оба — триггерит первый сработавший. Кулдаун 20 секунд общий.

GPS — как сейчас

WiFi — два режима

Режим 1: WiFi-картина (fingerprint)

Режим 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. Три уровня подтверждения

Хозяин решает НУЖНО ЛИ подтверждение. Способ подтверждения личности клиент выбирает сам из доступных на устройстве.

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

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

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

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

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

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

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

  1. ГДЕ — GPS / WiFi fingerprint / подключение
  2. КТО — авто / подтверждение / биометрия
  3. КОГДА — временное окно
Входная дверь: WiFi fingerprint + подтверждение личности + 07:00-23:00 — серьёзнее большинства «умных замков».

7. Хозяин vs гость — разделение настроек

ПараметрХозяин (свои)Хозяин → гостямГость
Тип объектаВыбираетПередаётПолучает
КоординатыСтавит самОпционально (предупреждение)Ставит сам или получает
Радиус GPSЗадаётПередаётПолучает
WiFi fingerprintСканируетНикогдаСканирует сам
WiFi мин. совпаденийЗадаётПередаётПолучает
Временное окноЗадаётПередаётПолучает
ПодтверждениеЗадаётПередаётПолучает
Вкл/выкл GPS
Вкл/выкл WiFi

8. UI в приложении

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

Первый шаг — выбор типа подключения:

┌────────────────────────────┐
│  Тип подключения           │
│                            │
│  📞  Звонок                │
│  Открытие телефонным       │
│  звонком                   │
│                            │
│  🌐  URL                   │
│  Отправка команды на       │
│  адрес устройства          │
│                            │
│  📟  Устройство            │
│  Подключение через Entrixy  │
│  (ESP32, Arduino)          │
└────────────────────────────┘

После выбора — модалка с настройками под выбранный тип.

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

┌──────────────────────────────────────────┐
│  Шлагбаум УГОЛ              📶 3/5  312м │
│  Свой • ●                                │
├──────────────────────────────────────────┤
│  [Редактировать]  [Иконка]               │
│  ─────────────────────────               │
│  [📍 Гео]   [📶 WiFi]                    │
└──────────────────────────────────────────┘

Модалка «📍 Гео»

Модалка «📶 WiFi»

◉ По WiFi-картине

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

Внизу: временное окно + подтверждение (общие с Гео)

Модалка webhook (🌐 URL)

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

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

ТриггерИндикатор
GPS«312 м»
WiFi fingerprint📶 деления (0-4 по совпадениям)
WiFi подключение📶 зелёная / серая
Webhook/Device● зелёный (онлайн) / серый (оффлайн)

9. Тарифные лимиты

Внутренние тарифные планы. Не видимы пользователю, но ограничения есть.

Дефолтный планЛимит
Объекты (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. БД — полная структура изменений

-- Тарифные планы
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;

-- Тип объекта и настройки
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;

-- Устройства (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. Гость подключается
   → guest_ok содержит has_avatar: true для объектов с иконкой

2. Клиент гостя проверяет: has_avatar=true, а локально иконки нет?
   → Да → запрашивает у сервера

3. Сервер не имеет иконки
   → шлёт хозяину через WS: {type: "avatar_request", number_id: 42}

4. Телефон хозяина
   → читает локальный файл
   → отправляет: {type: "avatar_data", number_id: 42, data: "base64..."}

5. Сервер пробрасывает гостю
   → {type: "avatar_data", number_id: 42, data: "base64..."}

6. Гость сохраняет локально, больше не запрашивает

7. Сервер ничего не сохранил — данные прошли транзитом

Граничные случаи

СитуацияПоведение
Хозяин оффлайнГость не видит иконку до появления хозяина онлайн. Дефолтная иконка.
50 гостей запрашивают одновременно50 × 5 КБ = 250 КБ через WS. Ничего.
Хозяин сменил иконкуhas_avatar_hash меняется → гости перезапрашивают
Хозяин удалил иконкуhas_avatar: false → гости показывают дефолтную
Приватность сохранена. Иконка шлагбаума — не чувствительная информация, но даже она не хранится на сервере. Транзит через WS, ноль на диске.

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

Один активный хозяин генерирует

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

Масштабирование

АбонентовОдновременно онлайн (~15%)WS-соединенийСервер
1 000~150~150Легко
10 000~1 500~1 500Легко
50 000~7 500~7 5001 воркер на пределе
100 000~15 000~15 0002-3 воркера

Workerman на 1 воркере: 5 000-10 000 одновременных соединений (~20 КБ на соединение).

Webhook и Device

Узкое место

Не WS, а MySQL. Но 250 000 INSERT/день (50К абонентов × 5 звонков) — MySQL на SSD держит без проблем.

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

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

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

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