Flagman: контракт веб-платформы и сетевого комплекса
Версия 0.2, черновик от 05.11.2026. Предложение веб-команды для обсуждения с Денисом. Основа: ТЗ v1.1, приложение А. Названия, поля и транспорт можно менять; важны данные и гарантии из раздела 2.
Часть решений в ТЗ v1.1 пока принята как допущения. Вопросы к технической команде собраны в разделе 8, новые: К-10…К-15.
Что изменилось с версии 0.1:
- Срок считается с активации устройства, появился пробный период 30 дней для обоих продуктов.
- Два продукта с разным путём трафика:
traffic_mode. - Клиент подписки $150 выбирает страну и город выхода.
- После окончания срока действует отсрочка 3 дня.
- Расчётный месяц считается по Москве.
- Один партнёр равен одному узлу, простой любого вида не оплачивается.
- Появился перенос партнёров, подключённых раньше платформы.
1. Кто за что отвечает
| Данные | Главная система |
|---|---|
| Пользователи, подписки, оплата, срок права доступа | Веб-платформа |
| Партнёры, реквизиты, начисления, выплаты | Веб-платформа |
| Правило «один пробный период на устройство» | Веб-платформа |
| Состояние устройств и узлов | Сетевой комплекс |
| Маршрутизация, распределение клиентов по узлам, серверы проекта | Сетевой комплекс |
| Свободные места по странам и городам | Сетевой комплекс |
| Подтверждённые интервалы доступности | Сетевой комплекс |
| Настройки роутера и удалённый доступ к нему | Сетевой комплекс |
Платформа не находится на пути трафика и не получает посещаемые адреса.
2. Обязательные гарантии
- Автономность. Сетевой комплекс получает срок действия права заранее и применяет его сам. Недоступность платформы не отключает клиентов и не мешает активации: срок истекает только по
valid_until, а при активации рассчитывается поterm_days_from_activation. - Идемпотентность. Повтор команды или события не меняет результат.
- Доставка событий не реже одного раза. Дубли и нарушение порядка допустимы, поэтому у каждого события есть
event_id, а у изменяемых объектовversion. - Окончательность месяца. Комплекс явно сообщает, что данные о доступности за месяц полные. Без этого расчёт и выплаты не запускаются.
- Сверка. Обе стороны могут получить полный список текущих объектов и исправить расхождения.
3. Общие правила
- HTTPS, JSON, UTF-8. Базовый путь
/v1, несовместимые изменения только в новой версии. - Время передаётся в UTC, формат ISO 8601. Интервалы полуоткрытые:
[started_at, ended_at). - Расчётный месяц считается по Москве (UTC+3). Например,
2026-11это[2026-10-31T21:00:00Z, 2026-11-30T21:00:00Z). Даты «1-е» и «3-е» в разделе 6 тоже по Москве. - Идентификаторы: непрозрачные строки до 64 символов.
license_idвыдаёт платформа,device_idиnode_idвыдаёт комплекс (К-1). - Аутентификация: взаимный TLS или подпись
HMAC-SHA256отtimestamp + "." + телос окном 5 минут. Ключи разные для каждой среды и направления. - Ограничение частоты: ответ
429с заголовкомRetry-After.
Формат ошибки:
{ "error": { "code": "license_not_found", "message": "Описание для журнала", "retryable": false } }
| Код HTTP | Что делает вызывающая сторона |
|---|---|
2xx | Операция принята |
400, 404, 409, 422 | Повтор бесполезен, ошибка попадает в панель интеграции |
401, 403 | Оповещение об ошибке доступа |
429, 5xx, таймаут | Повтор с нарастающей задержкой и тем же содержимым |
4. Команды платформы к сетевому комплексу
4.1 Установить право доступа
PUT /v1/entitlements/{license_id}
До активации срок ещё неизвестен, поэтому платформа передаёт его длительность:
{
"version": 1,
"state": "active",
"period_type": "trial",
"plan_code": "subscription_30d",
"traffic_mode": "partner_nodes",
"exit_location": { "country": "IN", "city": "Pune" },
"valid_until": null,
"term_days_from_activation": 33,
"activation_expires_at": "2026-11-20T10:30:00Z",
"max_devices": 1,
"activation_code_hash": "sha256:9f2c..."
}
После активации и при каждом продлении платформа передаёт точную дату:
{
"version": 3,
"state": "active",
"period_type": "paid",
"plan_code": "subscription_30d",
"traffic_mode": "partner_nodes",
"exit_location": { "country": "IN", "city": "Pune" },
"valid_until": "2027-01-07T10:30:00Z",
"max_devices": 1
}
state:activeилиrevoked. Отзыв и продление идут этим же методом.period_type:trialилиpaid, только для отображения и отчётов. Комплекс ориентируется на срок.plan_code:subscription_30d,annual_comfort,annual_premium.traffic_mode:partner_nodesдля подписки $150,project_serversдля годовой подписки (допущение, К-11).exit_location: только дляpartner_nodes. Смена точки выхода идёт этим же методом с новойversion(К-12).valid_untilуже включает отсрочку 3 дня: платформа сама прибавляет её к концу оплаченного периода.term_days_from_activationтоже с отсрочкой: 30 + 3 = 33.- Если заданы и
valid_until, иterm_days_from_activation, действуетvalid_until. - Запись с
versionменьше или равной сохранённой игнорируется и возвращает200. - Если в выбранном городе нет мест, комплекс присылает
entitlement.failedс кодомno_free_slots. - Ответ содержит применённую версию:
{ "license_id": "lic_01J9", "applied_version": 1, "device_id": null }
4.2 Заменить устройство
POST /v1/entitlements/{license_id}/release-device
Заголовок Idempotency-Key. Тело: { "reason": "router_replaced" }. Освобождает лицензию для новой активации. Срок при этом не меняется. Новый пробный период замена не даёт.
4.3 Зарегистрировать узел
PUT /v1/nodes/{partner_node_ref}
partner_node_ref создаёт платформа, ответ содержит код привязки для установки на роутер партнёра. Один партнёр равен одному узлу: платформа не отправит второй узел для того же partner_ref, пока первый не выведен.
{
"partner_ref": "prt_4k2",
"country": "IN",
"city": "Pune",
"router_model": "flint-2",
"declared_public_ip": "203.0.113.10",
"declared_downlink_mbps": 200
}
Ответ:
{ "partner_node_ref": "pn_91", "node_id": null, "claim_code": "8H2K-QP4M", "claim_expires_at": "2026-11-07T00:00:00Z", "status": "awaiting_claim" }
4.4 Привязать уже работающий узел
PUT /v1/nodes/{partner_node_ref}/link
Для партнёров, подключённых вручную раньше платформы (К-13). Заголовок Idempotency-Key. Тело: { "node_id": "node_12", "partner_ref": "prt_4k2" }. Код привязки не нужен, интервалы узла остаются доступны за прошлые месяцы.
4.5 Вывести узел из сети
POST /v1/nodes/{node_id}/retire
Заголовок Idempotency-Key. Тело: { "reason": "partner_terminated" }.
4.6 Точки выхода и ёмкость
GET /v1/exit-locations
{
"as_of": "2026-11-05T09:00:00Z",
"items": [
{ "country": "IN", "city": "Pune", "active_nodes": 8, "capacity_total": 40, "assigned_clients": 33, "free_slots": 7 },
{ "country": "IN", "city": "Mumbai", "active_nodes": 12, "capacity_total": 60, "assigned_clients": 50, "free_slots": 10 }
]
}
- Клиенты на пробном периоде учитываются в
assigned_clients: они занимают места наравне с оплатившими. - Платформа показывает для выбора только города с
free_slots > 0. Если мест нет нигде, показывает лист ожидания. - Годовая подписка (
project_servers) этих мест не занимает и ёмкостью не ограничивается (К-11).
4.7 Доступность узлов
GET /v1/availability/intervals?month=2026-11&cursor=...
{
"month": "2026-11",
"month_status": "open",
"items": [
{
"interval_id": "ivl_100",
"revision": 1,
"node_id": "node_12",
"started_at": "2026-10-31T21:00:00Z",
"ended_at": "2026-11-03T14:20:10Z",
"counted": true,
"exclusion_reason": null
}
],
"next_cursor": null
}
month_status:openилиclosed. Послеclosedданные считаются полными, позднее исправление приходит как новаяrevisionи попадает в следующий расчётный период.- У одного узла действующие интервалы не пересекаются.
- Интервал не выходит за границы месяца по Москве. Если узел работает на стыке месяцев, комплекс передаёт два интервала.
- Платим только за подтверждённое время. Сбой сети и плановые работы не оплачиваются, даже если партнёр не виноват.
counted = falseозначает, что интервал не оплачивается. Причина вexclusion_reason: например,quality_below_threshold,planned_maintenance,network_fault. Её видит партнёр в кабинете.- Как определяется подтверждённое время, решит Денис (К-4). Формат интервалов от этого не меняется.
4.8 Выгрузка для сверки
GET /v1/snapshots/{entitlements|devices|nodes}?cursor=...
Постраничный полный список текущих объектов. Платформа запрашивает его раз в сутки.
5. События сетевого комплекса к платформе
POST {адрес_платформы}/integrations/network/v1/events
{
"event_id": "evt_01JC8",
"type": "device.activated",
"occurred_at": "2026-11-05T09:12:00Z",
"schema_version": 2,
"data": {
"license_id": "lic_01J9",
"device_id": "dev_77a1",
"hardware_id": "hw_5c1e",
"router_model": "flint-2",
"software_version": "1.4.2",
"valid_until": "2026-12-08T09:12:00Z"
}
}
Ответ 2xx значит, что событие сохранено. Иначе комплекс повторяет доставку с нарастающей задержкой не менее 72 часов.
| Тип | Обязательные данные | Что делает платформа |
|---|---|---|
entitlement.applied | license_id, version | Закрывает команду в очереди |
entitlement.failed | license_id, version, code | Показывает ошибку в админке, при no_free_slots предлагает другой город |
device.activated | license_id, device_id, hardware_id, модель, версия ПО, valid_until | Начинает срок, проверяет пробный период, отмечает устройство активным |
device.activation_failed | license_id, code | Показывает клиенту следующий шаг |
device.online, device.offline | device_id, last_seen_at | Обновляет статус в кабинете |
node.claimed | partner_node_ref, node_id, фактический IP | Запускает ожидание проверки |
node.check_passed, node.check_failed | node_id, результаты, code | Активирует узел или показывает причину |
node.online, node.offline | node_id, changed_at | Обновляет кабинет партнёра, оповещает о простое |
capacity.changed | поля города из 4.6 | Открывает или закрывает продажи, обновляет список городов |
availability.interval_corrected | interval_id, revision, поля интервала | Создаёт корректировку |
availability.month_closed | month | Запускает расчёт начислений |
События online и offline служат только для отображения. Деньги считаются только по интервалам из 4.7.
6. Основные сценарии
Подключение с пробным периодом. Клиент выбирает тариф и, для подписки $150, город → PUT entitlements с term_days_from_activation → клиент ставит ОС и вводит код → комплекс проверяет код, сам считает valid_until → device.activated → платформа записывает начало срока и подтверждает дату новой version.
Второй пробный период. Пробный период даётся один раз на учётную запись и на устройство. Платформа проверяет hardware_id в device.activated. Если устройство уже использовало пробный период: PUT entitlements с state: revoked, клиент получает счёт и подключается после оплаты (К-10).
Покупка после пробного периода и продление. Оплата USDT подтверждена блокчейном, оплата картой или по счёту подтверждена сотрудником → PUT entitlements с новым valid_until и увеличенной version. Период: 30 дней или год. Если события подтверждения нет, команда повторяется.
Неоплата. Отдельная команда не требуется: комплекс сам прекращает доступ по valid_until, в котором уже учтена отсрочка 3 дня. После этого роутер работает как обычный, без VPN.
Подключение узла. PUT nodes → партнёр вводит код → node.claimed → проверка → node.check_passed → интервалы доступности начинают учитываться.
Перенос первых партнёров. Узлы подключены вручную раньше платформы → комплекс передаёт реестр (К-13) → платформа создаёт партнёров → PUT nodes/.../link → интервалы загружаются с начала работы узла.
Закрытие месяца. 1-го числа по Москве комплекс завершает учёт → availability.month_closed не позже конца 3-го числа → платформа считает начисления. Если события нет, расчёт останавливается с оповещением, выплаты не запускаются.
7. Что нужно для интеграции
- Тестовый стенд с отдельными ключами и тестовыми устройством и узлом.
- Описание кодов ошибок и лимитов.
- Адрес для оповещений о плановых работах и несовместимых изменениях минимум за 14 дней.
Пока стенда нет, платформа работает с эмулятором этого контракта. Реальный API подключается заменой адаптера.
8. Вопросы к технической команде
| № | Вопрос | Что предлагаем |
|---|---|---|
| К-1 | Кто создаёт идентификаторы устройства и узла? | Комплекс; лицензию создаёт платформа |
| К-2 | Как роутер подтверждает лицензию: код от платформы или иначе? | Одноразовый код, в комплексе только его хеш |
| К-3 | Может ли комплекс сам считать срок при активации и отключать доступ без связи с платформой? | Да, см. гарантию 1 |
| К-4 | Как определяется доступность: частота проверок, что считается простоем? | Опишите текущий механизм; поля counted и exclusion_reason закрывают исключения |
| К-5 | Можно ли гарантировать закрытие месяца до конца 3-го числа по Москве? | Да, иначе выплата 5-го числа под угрозой |
| К-6 | Как считается ёмкость: фиксированные 5 клиентов или динамически? Когда подтвердите 5? | Комплекс отдаёт готовое значение free_slots по городам |
| К-7 | Настройки маршрута, лимитов и блокировок нужны в кабинете? | Нет, это зона комплекса и роутера |
| К-8 | Что удобнее для подписи: взаимный TLS или HMAC? | HMAC проще для старта |
| К-9 | Когда будут контракт и тестовый стенд? | Нужна конкретная дата, сейчас ответ «на днях» |
| К-10 | Есть ли у роутера постоянный идентификатор, который не меняется при переустановке ОС? | Передавать его как hardware_id, без него пробный период можно получать повторно |
| К-11 | Годовая подписка идёт через серверы проекта? Сколько клиентов они держат? | Да, traffic_mode: project_servers, без лимита мест на стороне платформы. Если через узлы партнёров, нужно пересмотреть цену |
| К-12 | Где клиент выбирает страну и город: в кабинете или на роутере? Можно ли менять выбор и как часто? Какие страны, кроме Индии, появятся? | В кабинете, смена через exit_location |
| К-13 | Как передать первых 20 партнёров, если они подключатся раньше платформы? Хранятся ли их интервалы с первого дня? | Реестр узлов с контактами партнёра, интервалы с начала работы |
| К-14 | Какие 2 модели роутеров уже опробованы и где лежат сборки? | Коды и названия моделей, ссылки и контрольные суммы |
| К-15 | Что именно даёт удалённый доступ к роутерам клиента? | Короткое описание для оферты и политики данных |