# ADR-001: Архитектурные решения системы автоматизированной аренды оборудования

Дата: 2026-03-25

---

## 1. PocketBase (Go BaaS) с кастомными Go-хуками

**Status:** Accepted (обновлено, см. research-14)

**Context:** Команда: 1 fullstack-разработчик + ИИ-агенты. Исследование 80+ платформ (research-14) показало, что BaaS-подход экономит ~518K руб. и ~38 дней разработки по сравнению с написанием монолита с нуля. PocketBase — Go-фреймворк с auth, CRUD, realtime SSE, admin UI из коробки.

**Decision:** PocketBase как фундамент (auth, CRUD API, admin UI, realtime SSE, SQLite). Кастомная бизнес-логика (rental state machine, платежи Т-Банк/CloudPayments, MQTT bridge к NanoMQ) через Go-хуки (70+ event hooks). Один бинарник, ~200 MB RAM.

**Consequences:**
- Экономия ~20 дней разработки: auth, CRUD, admin panel, realtime — из коробки
- SQLite WAL — достаточно для <100 постаматов, бэкап = копирование файла
- Деплой = копирование одного бинарника + `docker compose up`
- Расширяемость нативным Go — полный контроль над бизнес-логикой
- Риск: SQLite — один writer, при >100 постаматов рассмотреть миграцию на PostgreSQL (через Turso/LiteStream)
- Риск: PocketBase — молодой проект. Митигация: open-source (MIT), Go-код полностью контролируем

**Alternatives Considered:**
- Модульный монолит Go с нуля — на ~38 дней дольше, дублирует готовые решения (auth, CRUD, admin)
- Supabase — 4-8 GB RAM, PostgreSQL, требует managed-инфраструктуру
- Appwrite — 4 GB RAM, Docker-зависимость, избыточен
- Микросервисы — оверхед для одного разработчика

---

## 2. Flutter 3 для мобильного приложения арендатора

**Status:** Accepted

**Context:** Нужно приложение для iOS и Android с поддержкой BLE (идентификация постамата) и NFC. Целевая аудитория включает пользователей low-end устройств.

**Decision:** Flutter 3 — единая кодовая база для обеих платформ.

**Consequences:**
- Один код — быстрая разработка и синхронный релиз на обе платформы
- Стабильная работа BLE/NFC через зрелые плагины (flutter_blue_plus, nfc_manager)
- Предсказуемая производительность на бюджетных Android-устройствах
- Риск: нативные edge-cases BLE могут потребовать platform channels

**Alternatives Considered:**
- React Native — менее стабильная работа с BLE, больше нативных зависимостей
- Два нативных приложения — двойные затраты на разработку и поддержку

---

## 3. Headless-постамат (без тачскрина) — весь UI на смартфоне

**Status:** Accepted (пересмотрено, см. ADR-003)

**Context:** Первоначально планировался терминал PAX IM30 с тачскрином и Kotlin-приложением (5 экранов). После анализа в ADR-003 принято решение о headless-модели: постамат работает без экрана, весь пользовательский интерфейс — в мобильном приложении (Flutter). Взаимодействие через QR-код на корпусе (идентификация постамата) + BLE (proximity-подтверждение).

**Decision:** Постамат — headless-устройство (открытый стеллаж с полками). Экран, PAX IM30, Kotlin-приложение не требуются. Обратная связь пользователю — через мобильное приложение и звуковой сигнал.

**Consequences:**
- Снижение стоимости постамата на 30-50K руб. (нет экрана, нет PAX)
- Уменьшение точек отказа и вандализма (экран — самый уязвимый компонент)
- Упрощение ПО: нет Kotlin-приложения, нет kiosk-mode, нет PAX SDK
- Мобильное приложение (Flutter) — единственный UI, должен покрывать все сценарии
- Риск: пользователи без смартфона или с разряженным телефоном не смогут воспользоваться сервисом
- Митигация: целевая аудитория (B2C, аренда оборудования) предполагает наличие смартфона

**Alternatives Considered:**
- ~~Kotlin + PAX IM30~~ — отвергнуто: избыточно, дорого, увеличивает поверхность атаки и вандализма
- Минималистичный дисплей (e-ink/LCD) — рассмотрено как P3, может быть добавлено позже

---

## 4. Core Service (Go) на RPi CM4/CM5 + ESP32 slave

**Status:** Accepted (обновлено, см. ADR-003)

**Context:** Постамат представляет собой открытый стеллаж с полками и ремнями-фиксаторами (стрепами), BLE для идентификации постамата. Нужна автономная работа при потере связи с сервером. Аппаратная платформа — RPi CM4/CM5 на промышленном carrier board (см. ADR-003, Вариант C1).

**Decision:** Двухуровневая архитектура:
- **RPi CM4/CM5** — Go Core Service: MQTT клиент (TLS), SQLite WAL (offline), BLE через BlueZ + nRF52840 USB dongle, опциональная IP-камера, fleet management (SSH/Ansible)
- **ESP32 slave** — телеметрия (температура, влажность, напряжение), контроль состояния полок, акселерометр/тампер, watchdog на RPi, MQTT. Связь с RPi через UART. **Без BLE, без MQTT credentials, без ключей** — полная изоляция

**Consequences:**
- Постамат работает автономно для возвратов оборудования (SQLite WAL на Linux)
- Go — один бинарник без зависимостей, удобное обновление
- BLE изолирован на nRF52840 dongle → CVE ESP32 BLE неактуальны
- Компрометация ESP32 ≠ компрометация постамата (нет секретов на ESP32)
- RPi CM4: 4GB RAM, полноценный Linux, USB, mPCIe (4G LTE)
- Риск: RPi — не промышленный класс. Митигация: carrier board с расширенным температурным диапазоном (Pineboards, EmbeddedPi)
- Риск: логика offline-очереди требует тестирования конфликтов синхронизации

**Alternatives Considered:**
- ~~Индустриальный ПК (Violanta/ESI)~~ — дороже, ESI отвергнут (нет BLE, vendor lock-in), Violanta избыточен
- ESP32 standalone (без RPi) — ограничен для продакшена (4MB flash, нет полноценной СУБД)
- Python на RPi — медленнее, больше зависимостей

---

## 5. Платежи: Т-Банк СБП (основной) + CloudPayments (залоговая модель)

**Status:** Accepted (обновлено, см. research-11)

**Context:** Исследование платёжных провайдеров (research-11) показало: комиссия СБП 0.4% — регуляторный минимум ЦБ РФ, все провайдеры дают одинаковую ставку. Т-Банк имеет лучшее API для вендинга. СБП не поддерживает холдирование — для залоговой модели нужен карточный эквайринг.

**Decision:** Два провайдера:
- **Т-Банк СБП (0.4%)** — основной метод для фиксированных тарифов (предоплата). Go SDK: `nikita-vanyasin/tinkoff`. QR-код генерируется в приложении.
- **CloudPayments (2.5-3.5%)** — fallback для залоговой модели (холд/confirm/void), рекуррентные платежи, привязка карты.

**Consequences:**
- Комиссия снижается с 2.5-3.5% до 0.4% для 80%+ транзакций (фиксированные тарифы)
- СБП — привычен для 90%+ пользователей РФ (все банки поддерживают)
- Два провайдера = resilience, абстракция платёжного слоя в Go-хуках PocketBase
- Ограничение: СБП не поддерживает холды — залоговые сценарии через CloudPayments

**Alternatives Considered:**
- Только CloudPayments — дороже в 6-9x по комиссии для фиксированных тарифов
- ЮKassa — менее удобный API для залоговой модели
- SberPay QR (0.3%) — только для клиентов Сбера, отдельная интеграция

---

## 6. Т-Банк СБП 0.4% — основной метод оплаты

**Status:** Accepted (обновлено)

**Context:** С 01.01.2026 комиссии за эквайринг облагаются НДС 22%: СБП 0.4% → фактически 0.49%, карты 2.5% → фактически 3.05%. Максимизация доли СБП-платежей — главная стратегия снижения расходов.

**Decision:** Т-Банк СБП — единственный метод для фиксированных тарифов (почасовая, посуточная аренда). QR-код генерируется через Т-Банк GetQR API, отображается в приложении. Пользователь сканирует QR банковским приложением. Webhook (PaymentNotification) подтверждает оплату.

**Consequences:**
- 0.4% (0.49% с НДС) — минимальная комиссия на рынке для нашего МСС-кода
- 90%+ пользователей смогут оплатить (СБП поддерживают все банки РФ)
- Нет привязки карты — ниже трение, быстрее конверсия
- Ограничение: нет холдов → залоговая модель только через CloudPayments (§5)

**Alternatives Considered:**
- CloudPayments для всех сценариев — комиссия 2.5-3.5%, в 6-9x дороже
- ЮKassa — те же 0.4% СБП, но эквайринг 2.8-3.5%
- SberPay QR (0.3%) — только клиенты Сбера, отдельная интеграция

---

## 7. Kit Online — облачная фискализация

**Status:** Accepted (пересмотрено)

**Context:** 54-ФЗ требует фискализации расчётов. Установка физического ФР на каждый постамат — дорого (~30 000 руб./шт.) и сложно в обслуживании.

**Decision:** Облачная касса Kit Online (251 ₽/мес). Чеки формируются через API серверной части. Минимальная стоимость среди облачных касс.

**Consequences:**
- Нет физического ФР на постамате — экономия на оборудовании и обслуживании
- Масштабирование без дополнительных затрат на железо
- 251 ₽/мес vs АТОЛ Онлайн 3 000 ₽/мес — экономия ~33 000 ₽/год
- Риск: зависимость от доступности Kit Online — смягчается retry-механизмом с очередью

**Alternatives Considered:**
- АТОЛ Онлайн (3 000 ₽/мес, "Вендинг") — в 12 раз дороже при том же функционале
- Физический ФР на каждом постамате — дорого, сложно обслуживать
- OrangeData — менее распространён, меньше документации

---

## 8. Фискальный чек только при списании, не при холде

**Status:** Accepted

**Context:** Холдирование средств на карте — это не расчёт в терминах 54-ФЗ. Чек должен формироваться в момент фактического списания (confirm) или при отмене холда (void — чек не нужен).

**Decision:** Чек пробивается только при confirm (подтверждение списания). При void (отмена холда) чек не формируется.

**Consequences:**
- Полное соответствие 54-ФЗ
- Упрощение логики: один момент фискализации
- Чек возврата — при частичном списании залога (разница возвращается)

**Alternatives Considered:**
- Чек при холде + чек возврата при отмене — избыточно, не соответствует требованиям закона

---

## 9. Верификация оборудования: фото с телефона пользователя

**Status:** Accepted (пересмотрено)

**Context:** Нужна идентификация оборудования на полке при выдаче и возврате. Защита от подмены. Постамат — открытый стеллаж с ремнями-фиксаторами, без закрытых ячеек, без весового контроля. Верификация должна быть простой и не требовать дополнительного оборудования на постамате.

**Decision:** Двухуровневая верификация через мобильное приложение:
1. **Фото с камеры телефона пользователя** — при выдаче и возврате пользователь делает фото оборудования через приложение (электронный акт приёма-передачи)
2. **Подтверждение пользователя** в приложении — чеклист состояния + фото

Опционально: 1 обзорная IP-камера на весь стеллаж (для безопасности и разрешения споров).

**Consequences:**
- Минимум оборудования на постамате — снижение стоимости и точек отказа
- Фото с телефона = доказательная база при спорах
- Подтверждение пользователя = юридически значимый акт приёма-передачи
- MVP: ручная верификация оператором (фото в Telegram). v2: ML-модель (CLIP/ResNet)
- Риск: пользователь может сфотографировать не тот предмет. Митигация: геолокация + BLE proximity + timestamp
- Риск: ML-верификация требует обучения. Митигация: на MVP — ручная проверка оператором

**Alternatives Considered:**
- ~~UHF RFID~~ — метки срываются, переклеиваются, не определяют повреждения, дороже
- ~~Камеры OV5640 в каждой ячейке~~ — избыточно для открытых полок, дорого (16 камер + USB Hub)
- ~~Тензодатчики HX711~~ — не применимы к открытым полкам с ремнями
- QR-коды на оборудовании — легко подделать, не определяют подмену/повреждение

---

## 10. Физическая фиксация оборудования

**Status:** Accepted (пересмотрено)

**Context:** Постамат — открытый стеллаж. Оборудование размещается на полках и фиксируется ремнями (стрепами). Электромеханические замки удалены из конструкции — полки открытые, доступ к оборудованию не блокируется электроникой.

**Decision:** Ремни-фиксаторы (стрепы) на каждой полке. Ручное снятие/установка пользователем. Не требуют электропитания, управляющей электроники или реле.

**Характеристики:**
- Материал: нейлон, ширина 25-50 мм, пряжка с быстрым расстёгиванием
- Фиксация: оборудование прижимается к полке, предотвращая случайное падение
- Снятие: пользователь расстёгивает ремень вручную, забирает оборудование
- Установка: при возврате — кладёт оборудование на полку, застёгивает ремень

**Consequences:**
- Радикальное упрощение конструкции: нет замков, реле, герконов, ESP32 GPIO для управления замками
- Нулевое энергопотребление системы фиксации
- Нет точек электромеханического отказа
- Быстрая выдача/возврат — не нужно ждать команды на открытие
- Риск: оборудование физически доступно любому → компенсируется видеонаблюдением (обзорная IP-камера), BLE proximity-подтверждением, фото-верификацией
- Риск: вандализм/кража с открытой полки → компенсируется размещением в контролируемых зонах (ТЦ, коворкинги, охраняемые объекты)

**Alternatives Considered:**
- ~~Электромоторные замки (fail-secure)~~ — избыточны для новой концепции открытых полок, увеличивают стоимость и сложность
- ~~Электромагнитные замки~~ — не применимы к открытой конструкции
- Тросовые замки с PIN-кодом — рассмотрены как P2 для дорогого оборудования

---

## 11. MQTT over TLS (NanoMQ) для связи с постаматами

**Status:** Accepted (обновлено, см. research-14)

**Context:** Постаматы подключены через 4G/LTE. Нужен надёжный двунаправленный канал с минимальным overhead, поддержкой QoS и работой при нестабильном соединении. Исследование (research-14) показало, что NanoMQ избыточен для <100 постаматов (500-800 MB RAM), NanoMQ занимает 10-30 MB.

**Decision:** MQTT v5 over TLS 1.3 с брокером NanoMQ (C, open-source). QoS 1 для команд управления, QoS 0 для телеметрии. Топики: `locker/{id}/cmd`, `locker/{id}/status`, `locker/{id}/telemetry`. NanoMQ запускается на том же VPS в Docker Compose.

**Consequences:**
- Малый overhead (~2 байта заголовок) — экономия трафика на 4G
- QoS гарантирует доставку критических команд (управление выдачей оборудования)
- NanoMQ: 10-30 MB RAM — помещается на VPS 2 GB вместе с PocketBase
- Полная поддержка MQTT 5.0, TLS 1.3, WebSocket, ACL
- Риск: нет кластеризации. Митигация: при >100 постаматов мигрировать на NanoMQ/HiveMQ
- Риск: MQTT не предназначен для передачи больших объёмов данных — обновления прошивки через HTTP

**Alternatives Considered:**
- NanoMQ — отвергнут: 500-800 MB RAM, кластеризация не нужна при <100 устройств
- WebSocket — больше overhead, нет встроенного QoS
- gRPC — сложнее для IoT-сценариев, не оптимален для нестабильного соединения
- HTTP polling — неэффективен, задержка доставки команд

---

## 12. SQLite WAL (PocketBase managed)

**Status:** Accepted (обновлено, см. research-14)

**Context:** Финансовые операции (холды, списания, залоги) требуют ACID-гарантий. PocketBase использует SQLite в WAL-режиме — полноценный ACID с одним бинарником.

**Decision:** SQLite в WAL-режиме, управляемый PocketBase. Одна БД для всех данных (users, equipment, rentals, payments, telemetry). Redis не нужен — PocketBase имеет встроенный realtime (SSE) и кеширование в памяти Go-процесса.

**Consequences:**
- ACID через SQLite WAL — нет риска двойного списания или потери транзакций
- Нулевой overhead на отдельные процессы БД (нет PostgreSQL, нет Redis)
- Бэкап = копирование одного файла (`pb_data/data.db`)
- PocketBase автоматически управляет миграциями, индексами, vacuum
- Ограничение: один writer — при >100 постаматов и высокой конкурентности рассмотреть миграцию на PostgreSQL (через Turso/LiteStream)
- Ограничение: нет advisory locks — блокировка слотов через `BEGIN IMMEDIATE` + уникальные индексы

**Alternatives Considered:**
- PostgreSQL 16 — избыточен для MVP, +3-5K руб./мес. managed, отдельный процесс
- Redis — не нужен: PocketBase SSE заменяет pub/sub, Go in-memory cache заменяет кеш
- MongoDB — нет ACID по умолчанию, не подходит для финансовых данных

---

## 13. Offline-режим: возврат — да, новая аренда — нет

**Status:** Accepted

**Context:** Постамат может потерять связь с сервером (сбой 4G). Нужно определить, какие операции допустимы offline.

**Decision:** При потере связи постамат принимает возврат оборудования (подтверждает фиксацию на полке, сохраняет факт возврата в локальную очередь). Новая аренда offline невозможна — нельзя подтвердить оплату.

**Consequences:**
- Пользователь не застревает с оборудованием при сбое связи
- Нет финансового риска: выдача без подтверждения оплаты исключена
- Синхронизация возвратов при восстановлении связи (confirm/void холда)
- Ограничение: пользователь не может взять оборудование при отсутствии связи

**Alternatives Considered:**
- Полный offline (аренда + возврат) — финансовый риск: выдача без гарантии оплаты
- Полный запрет offline — пользователь не может вернуть оборудование, ему продолжают начислять арендную плату

---

## 14. Аппаратная платформа: RPi CM4/CM5 + ESP32 + nRF52840

**Status:** Accepted (пересмотрено, см. ADR-003)

**Context:** Первоначально планировались индустриальные ПК российских производителей (Violanta, ESI). После анализа в ADR-003: ESI отвергнут (нет BLE, vendor lock-in), Violanta избыточен для headless-постамата. RPi CM4/CM5 на промышленном carrier board обеспечивает необходимую надёжность при меньшей стоимости.

**Decision:** Кастомная сборка на базе доступных компонентов:
- **RPi CM4/CM5** (Великобритания) — основной SBC, Linux, Go Core Service
- **ESP32-S3** (Китай, Espressif) — slave-контроллер телеметрии и датчиков
- **nRF52840 USB dongle** (Норвегия, Nordic) — BLE-радио
- **Carrier board** (Pineboards/EmbeddedPi) — промышленный адаптер для CM4
- Ремни-фиксаторы, корпус — российские/китайские поставщики

**Consequences:**
- Нет vendor lock-in — каждый компонент заменяем
- RPi CM4 широко доступен через российских дистрибьюторов (Амперка, Чип и Дип, МастерКит)
- ESP32 и nRF52840 — массовые компоненты, доступны через AliExpress, LCSC, российских дистрибьюторов
- Санкционные риски минимальны: компоненты потребительского класса, не попадают под ограничения
- Нет российской сертификации «из коробки» — нужна при серийном производстве
- Корпус постамата — заказ на российском производстве (сталь 2 мм)

**Alternatives Considered:**
- ~~Violanta/ESI (российские IPC)~~ — ESI: нет BLE, vendor lock-in. Violanta: избыточен, дороже, не даёт преимуществ для headless-модели
- ~~Только ESP32 (без RPi)~~ — ограничен для продакшена: 4MB flash, нет полноценной СУБД, сложное fleet management
- Intel NUC — избыточно по мощности, дороже RPi CM4

---

## 15. Верификация: телефон + SMS (MVP), паспорт для > 10 000 руб. (v2)

**Status:** Accepted

**Context:** Нужна идентификация пользователя для защиты от мошенничества. Излишняя верификация снижает конверсию.

**Decision:** MVP: регистрация по номеру телефона + SMS-код. V2: для оборудования стоимостью > 10 000 руб. — дополнительная верификация через паспортные данные (или банковский ID).

**Consequences:**
- Минимальное трение при регистрации на MVP — высокая конверсия
- Постепенное усиление верификации для дорогого оборудования
- SMS — привычный для пользователей механизм
- Риск: SMS-верификация уязвима к SIM-swap атакам — для v2 рассмотреть push-уведомления

**Alternatives Considered:**
- Паспорт для всех с самого начала — высокое трение, низкая конверсия на MVP
- Только email — слабая привязка к личности, легко создать множество аккаунтов

---

## 16. Prometheus + Grafana + Sentry для мониторинга

**Status:** Accepted

**Context:** Распределённая система (сервер + N постаматов) требует мониторинга метрик, алертинга и отслеживания ошибок в реальном времени.

**Decision:** Prometheus — сбор метрик (серверные + с постаматов через pushgateway). Grafana — визуализация и алерты. Sentry — трекинг ошибок и исключений в Go-сервисах и Flutter-приложении.

**Consequences:**
- Open-source стек — нет лицензионных затрат
- Prometheus + Grafana — де-факто стандарт, огромная экосистема экспортеров
- Sentry — мгновенные уведомления об ошибках с полным стектрейсом
- Единый дашборд: состояние полок, статус связи, финансовые метрики
- Риск: pushgateway для постаматов — не идеален для long-lived targets, при масштабировании рассмотреть remote write

**Alternatives Considered:**
- ELK Stack — тяжелее, больше ресурсов, избыточен для метрик
- Datadog/New Relic — платные, дорого при росте количества постаматов
- Zabbix — менее гибкий для cloud-native метрик

---

## 17. Модуль согласий (Consent Service) для 152-ФЗ

**Status:** Accepted

**Context:** С 01.09.2025 согласие на обработку ПД должно быть отдельным документом (не частью оферты). Требуется отдельное согласие на каждую цель обработки ПД. Штраф за нарушение — до 1,5 млн руб. (повторное). Платформа обрабатывает телефон, email, геолокацию, историю аренд, платёжные токены — каждая категория требует обоснования и явного согласия.

**Decision:** Выделить Consent Service как Go-хук в PocketBase. Функции: получение отдельного согласия на каждую цель (аренда, маркетинг, аналитика), логирование фактов получения (timestamp, версия текста, IP, device ID), механизм отзыва согласия, автоматическое удаление данных при отзыве, журнал обращений субъектов ПД (срок ответа — 30 дней).

**Consequences:**
- Полное соответствие 152-ФЗ в редакции от 01.09.2025
- Готовность к проверкам РКН (журнал, audit log обращений к ПД)
- Дополнительная разработка: ~2 недели на MVP-версию модуля
- UX: дополнительный экран при регистрации (чекбоксы согласий)

**Alternatives Considered:**
- Согласие в составе оферты — незаконно с 01.09.2025
- Внешний SaaS для управления согласиями (b-152.ru) — vendor lock-in, не контролируем латентность

---

## 18. VPS в РФ (Aeza / Yandex Cloud) вместо Kubernetes

**Status:** Accepted (обновлено, см. research-14)

**Context:** 242-ФЗ обязывает хранить ПД граждан РФ на серверах в РФ. С 01.07.2025 первичный сбор на зарубежных серверах = нарушение. Штраф за нарушение локализации — до 18 млн руб. PocketBase + NanoMQ + Go-бэкенд укладываются в 1-1.5 GB RAM — Kubernetes избыточен.

**Decision:** VPS 2 GB RAM в ЦОД РФ. Основной провайдер — Aeza (~593 руб./мес., ЦОД Москва). Резервный — Yandex Cloud VM. Docker Compose для оркестрации (PocketBase + NanoMQ). Object Storage (Yandex/S3-совместимый) для фото и бэкапов.

**Consequences:**
- Полное соответствие 242-ФЗ (ЦОД в Москве)
- Стоимость ~593-2 000 руб./мес. vs 9 400-15 500 руб./мес. на Yandex Cloud MK8S (экономия в 5-10x)
- Простота: один сервер, `docker compose up`, SSH для управления
- VPS 2 GB обслуживает до ~100 постаматов
- Ограничение: нет автоскейлинга — при >100 постаматов рассмотреть Yandex Cloud MK8S
- Секреты: `.env` файл + Docker secrets (Vault/Lockbox — избыточны для одного сервера)

**Alternatives Considered:**
- Yandex Cloud MK8S — отвергнут: Kubernetes избыточен для одного бинарника, 9-15K руб./мес.
- Hetzner/AWS — не соответствует 242-ФЗ (ЦОД за рубежом)
- Contabo — дешевле, но ЦОД в ЕС (не подходит для 242-ФЗ)
- Selectel — резервный вариант, дороже Aeza

---

## 19. Flutter 3 для мобильного приложения (подтверждение после анализа Tauri)

**Status:** Accepted (подтверждено повторно)

**Context:** Рассматривался вариант Tauri Mobile (SPA + нативная обёртка) как альтернатива Flutter для переиспользования веб-кода. Проведён детальный анализ (docs/experts/11-tauri-mobile-stores.md).

**Decision:** Подтверждён выбор Flutter 3. Tauri Mobile отклонён.

**Причины отклонения Tauri:**
- BLE — только community-плагин с одним мейнтейнером (критический путь для связи с постаматом)
- Push-уведомления (FCM/APNs) — нет официального плагина
- App Store — мало прецедентов Tauri-приложений, высокий риск отклонения по Guideline 4.2
- Маленькое сообщество, Rust-порог для кастомных плагинов
- Нет in-app payments, background tasks

**Consequences:**
- Зрелые плагины BLE (flutter_blue_plus), NFC (nfc_manager), push (firebase_messaging)
- Стандарт индустрии — 100% одобрение в сторах
- Нативный рендер — стабильная работа на low-end Android
- Потеря: невозможно переиспользовать SPA-код, нужна отдельная Dart-кодовая база

**Alternatives Considered:**
- Tauri Mobile — отклонено (см. выше)
- Capacitor/Ionic — компромиссный вариант (SPA + зрелые плагины), но WebView хуже на бюджетных устройствах
- React Native — менее стабильная работа с BLE

---

## 20. Платежи в приложении: СБП QR (основной) + CloudPayments SDK (залог)

**Status:** Accepted (обновлено)

**Context:** Основной метод оплаты — СБП QR (Т-Банк, 0.4%). Для залоговой модели (холды) нужен карточный эквайринг. Собственная PCI DSS сертификация стоит от 1 млн руб./год — неподъёмно для стартапа.

**Decision:** Два платёжных потока в приложении:
1. **СБП QR (основной, 80%+ транзакций)** — приложение запрашивает QR у бэкенда → Т-Банк Init API → GetQR → отображение SVG/PNG. Пользователь сканирует QR банковским приложением. Webhook подтверждает оплату.
2. **CloudPayments Mobile SDK (залог)** — для привязки карты и холдирования. Карточные данные шифруются на устройстве, сервер хранит только токены (AccountId + Token). PCI DSS scope out.

**Consequences:**
- 80%+ платежей — СБП QR (0.4%), без привязки карты, минимальное трение
- CloudPayments SDK — только для залоговых сценариев (auth/confirm/void)
- PCI DSS scope out — нет обязательств по сертификации
- CloudPayments имеет PCI DSS Level 1 (высший уровень)
- Поддержка 3D-Secure и рекуррентных платежей по токену
- Ограничение: два платёжных SDK в приложении — абстрагируется через PaymentService

**Alternatives Considered:**
- Только CloudPayments — дороже в 6-9x по комиссии для фиксированных тарифов
- Только СБП — невозможно: нет холдов для залоговой модели
- ЮKassa SDK — менее удобный API для залоговой модели

---

## 21. Механика депозита: автоматическое списание и ручной возврат

**Status:** Accepted

**Context:** Залоговая модель аренды требует удержания суммы на карте клиента. CloudPayments поддерживает холдирование (auth), но с ограничениями: Visa/Mastercard — макс. 7 дней, МИР — до 30 дней. Аренда может длиться дольше 7 дней, что делает чистый hold-модель недостаточным.

**Decision:** Гибридная модель в зависимости от срока аренды:

### Модель A: Аренда ≤ 7 дней (основной сценарий)

```
auth(залог) → ... аренда ... → confirm(стоимость аренды) + void(остаток)
```

- **При выдаче:** `auth` на сумму залога (холд)
- **При возврате:** `confirm` на фактическую сумму аренды (время × тариф), остаток автоматически снимается с холда (void)
- **При невозврате:** `confirm` на полную сумму залога
- **Фискализация:** чек только при `confirm` (не при auth/void)
- **Плюсы:** деньги клиенту не списываются до возврата, нет задержек возврата
- **Ограничение:** холд ≤ 7 дней (Visa/MC)

### Модель B: Аренда > 7 дней (длительная)

```
charge(полная сумма) → ... аренда ... → refund(разница)
```

- **При выдаче:** `charge` (списание) полной суммы залога + аренды за весь период
- **При возврате:** `refund` разницы между списанной суммой и фактической стоимостью аренды
- **При невозврате:** дополнительных списаний не требуется (уже списано)
- **Фискализация:** чек «приход» при charge, чек «возврат прихода» при refund
- **Плюсы:** нет ограничения по срокам
- **Минусы:** деньги списываются сразу, возврат занимает 1-10 рабочих дней (зависит от банка)

### Модель C: Рекуррентное продление (для открытых аренд)

```
auth(залог) → [каждые 7 дней: void + auth] → confirm(итог)
```

- **При выдаче:** `auth` на залог
- **Каждые 6 дней** (до истечения холда): `void` текущий холд → `charge` за прошедший период → `auth` новый холд
- **При возврате:** финальный `confirm`/`void`
- **Плюсы:** деньги не списываются авансом, нет ограничения по сроку
- **Минусы:** сложная логика cron, риск отклонения повторного auth при недостатке средств

### Рекомендуемая стратегия

| Сценарий | Метод оплаты | Модель | Обоснование |
|----------|:------------:|--------|-------------|
| Почасовая аренда (1-24 ч), фикс. тариф | **СБП QR (0.4%)** | Предоплата | Основной сценарий, минимальная комиссия |
| Посуточная аренда (1-7 дней), фикс. тариф | **СБП QR (0.4%)** | Предоплата | Фиксированная цена, без холда |
| Аренда с залогом (≤7 дней) | **CloudPayments (3%)** | **A** (auth/confirm/void) | Нужен холд, укладывается в лимит Visa/MC |
| Длительная аренда с залогом (>7 дней) | **CloudPayments (3%)** | **B** (charge + refund) | Холд >7 дней невозможен |
| Подписка / неопределённый срок | **CloudPayments (3%)** | **C** (рекуррентное продление) | Гибкость, сложность реализации → v2 |

### Сроки возврата средств

| Банк / платёжная система | Время возврата (refund) | Примечание |
|--------------------------|------------------------|------------|
| Visa / Mastercard | 1-5 рабочих дней | Зависит от банка-эмитента |
| МИР | 1-3 рабочих дня | Обычно быстрее |
| Сбербанк | 3-10 рабочих дней | Самый медленный |
| Тинькофф | 1-2 рабочих дня | Самый быстрый |
| void (отмена холда) | мгновенно | Деньги не были списаны |

### 54-ФЗ: когда пробивать чек

| Операция | Чек | Тип чека |
|----------|-----|----------|
| auth (холд) | **Нет** | Не является расчётом |
| void (отмена холда) | **Нет** | Не было расчёта |
| confirm (списание из холда) | **Да** | Приход |
| charge (прямое списание) | **Да** | Приход |
| refund (возврат) | **Да** | Возврат прихода |

### Влияние на комиссии

**СБП (Т-Банк):**
- Комиссия 0.4% (0.49% с НДС) от суммы платежа
- Возврат через API — комиссия за исходный платёж не возвращается

**CloudPayments (карты):**
- Комиссия ~2.5-3.5% берётся от **суммы списания** (confirm/charge)
- При void — комиссия **не взимается** (деньги не проходили)
- При refund — комиссия за исходный charge **не возвращается**
- Следствие: модель B (charge + refund) дороже модели A (auth/confirm) на комиссию от разницы

**Стратегия минимизации:** максимизировать долю СБП-платежей (0.4%) vs карточных (2.5-3.5%). Разница 6-9x.

**Consequences:**
- MVP реализует модель A (≤7 дней) — покрывает 90%+ сценариев почасовой/посуточной аренды
- Модель B добавляется при появлении длительных аренд
- Модель C (рекуррентное продление холда) — v2, требует надёжного cron и обработки отказов
- Клиенты предпочитают auth/void (деньги не списываются) — это конкурентное преимущество перед charge/refund

**Alternatives Considered:**
- Только charge + refund для всех сценариев — плохой UX: клиент видит списание полной суммы залога, возврат до 10 дней
- Только auth/confirm — ограничение 7 дней Visa/MC делает невозможными длительные аренды
- Сбербанк acquiring (СБП hold) — СБП не поддерживает холдирование, невозможно для залоговой модели

---

## 22. BLE для идентификации постамата и proximity-подтверждения

**Status:** Accepted (обновлено)

**Context:** Headless-постамат (открытый стеллаж, без экрана) требует механизма подтверждения присутствия пользователя рядом с постаматом. BLE используется НЕ для открытия замков (замков нет), а для идентификации постамата и proximity-верификации.

**Decision:** BLE (Bluetooth Low Energy) — канал для идентификации постамата и подтверждения присутствия пользователя (proximity). QR-код на корпусе — для идентификации постамата (пользователь сканирует → приложение определяет какой постамат). NFC — fallback (P2).

**Сценарий использования BLE:**
1. Пользователь подходит к постамату, приложение обнаруживает BLE-маяк постамата
2. Приложение подтверждает серверу, что пользователь находится рядом (proximity check)
3. Сервер разрешает операцию выдачи/возврата
4. Пользователь вручную снимает/устанавливает ремень, делает фото через приложение

**Реализация по фазам:**
- **MVP:** ESP32-S3 — нативный BLE 5.0 (NimBLE)
- **Продакшен:** nRF52840 USB dongle ($10-12) → BlueZ на RPi CM4. ESP32 отвечает только за телеметрию/датчики, BLE убран с ESP32

**Consequences:**
- BLE 5.0: дальность до 100 м (в помещении 10-30 м), энергоэффективность, поддержка на 99%+ смартфонов
- Подтверждение присутствия через BLE: <3 сек
- BLE-стек обновляется через BlueZ (`apt upgrade`) — не нужна перепрошивка
- nRF52840 (CryptoCell CC310) — минимум CVE, используется в Yale/August smart locks
- Риск: BLE может быть отключён на телефоне пользователя. Митигация: приложение запрашивает включение BLE

**Alternatives Considered:**
- NFC — дальность до 10 см, нужен точный контакт, неудобно для стеллажа
- Wi-Fi Direct — сложнее в реализации, выше энергопотребление, не все телефоны поддерживают
- Только QR-код (без BLE) — нет proximity-подтверждения, пользователь может отсканировать QR удалённо

---

## 23. Challenge-Response (Ed25519) для BLE proximity-подтверждения

**Status:** Accepted (обновлено)

**Context:** BLE используется для подтверждения присутствия пользователя рядом с постаматом (proximity). Замков нет — BLE НЕ управляет физическим доступом. Однако нужна защита от фальсификации присутствия (relay-атака: пользователь «подтверждает» присутствие, находясь далеко от постамата). Challenge-Response обеспечивает криптографическое подтверждение, что телефон действительно рядом с постаматом.

**Decision:** Challenge-response протокол поверх BLE GATT:
1. Приложение запрашивает выдачу/возврат оборудования у сервера (REST API)
2. Сервер проверяет GPS (телефон в радиусе 50 м от постамата), оплату, статус аренды
3. Сервер генерирует `signed_token = Ed25519(server_private_key, nonce + timestamp + locker_id + slot_id)`
4. Приложение передаёт `signed_token` постамату через BLE GATT Write
5. Постамат верифицирует подпись публичным ключом сервера, проверяет nonce (не использовался) + TTL (30 сек)
6. При успехе — подтверждает серверу присутствие пользователя, сервер разрешает операцию

**Consequences:**
- **Relay-атака невозможна**: криптографическое подтверждение proximity через BLE + GPS
- **Replay невозможен**: nonce одноразовый, постамат хранит последние N nonce
- **Подделка невозможна**: без приватного ключа сервера нельзя создать валидную подпись
- **Компрометация постамата бесполезна**: на устройстве только публичный ключ сервера
- **TTL 30 сек**: перехваченный токен истекает быстрее, чем атакующий может его использовать
- Per-device credentials (HW-NF12): каждый постамат имеет свой публичный ключ сервера, ротация через MQTT

**Alternatives Considered:**
- Только GPS без BLE — GPS легко подделать (mock location)
- HMAC вместо Ed25519 — требует shared secret на постамате (компрометация = полный доступ)
- Только QR-код — нет криптографического подтверждения proximity

---

## 24. Верификация оборудования (фото с телефона + опциональная обзорная камера)

**Status:** Accepted (пересмотрено)

**Context:** При аренде через постамат нет живого контакта между оператором и пользователем. Постамат — открытый стеллаж с ремнями-фиксаторами, без закрытых ячеек. Нужен механизм, который защищает обе стороны: пользователя — от получения сломанного/не того оборудования, бизнес — от претензий «оно уже было сломано» и подмены при возврате. Верификация — через фото с камеры телефона пользователя.

**Decision:** Двухэтапная верификация с фотодоказательствами через мобильное приложение.

### Этап 1: Загрузка оператором

Оператор размещает оборудование на полке и фиксирует состояние:
1. Фото предмета с телефона (оператор) → загружается в админ-панель
2. Чеклист состояния: корпус целый, комплектация полная, включается/работает
3. Фиксация оборудования ремнём на полке
4. Всё сохраняется на сервере: фото, чеклист, timestamp

### Этап 2: Выдача пользователю

1. Пользователь видит в приложении **карточку предмета**: фото оператора, описание, комплектация, статус проверки, дата загрузки
2. Бронирует, оплачивает (холд), подходит к постамату
3. BLE proximity подтверждает присутствие пользователя у постамата
4. **Экран подтверждения в приложении** (обязательный):
   - Чеклист: «Предмет соответствует описанию», «Корпус без повреждений», «Комплектация полная»
   - Кнопка «Сфотографировать» — пользователь делает фото с телефона (обязательно)
   - **«Всё ОК, забираю»** → аренда стартует, таймер пошёл
   - **«Есть проблема»** → фото + описание → алерт оператору (Telegram). Пользователь может: взять со скидкой, отказаться (void холда), подождать решения
5. Пользователь расстёгивает ремень, забирает оборудование
6. **Без подтверждения аренда не начинается** — это электронный акт приёма-передачи

### Этап 3: Возврат

1. Пользователь нажимает «Вернуть» в приложении → BLE proximity подтверждает присутствие
2. Кладёт предмет на полку, застёгивает ремень
3. **Фото с телефона** (обязательно) — пользователь фотографирует оборудование на полке
4. Приложение отправляет фото на сервер
5. Решение:

| Фото возврата | Действие |
|:---:|----------|
| Похоже на эталон (>85%) | Автоматический confirm, завершение аренды |
| Не похоже / подозрительно | Алерт оператору, аренда приостановлена |
| Нет фото | Аренда не завершается, напоминание пользователю |

### 24.1 Опциональная обзорная камера

**Решение:** 1 IP-камера на весь стеллаж (для безопасности и разрешения споров).

**Характеристики:**
- Тип: IP-камера (PoE или Wi-Fi), 2-5 MP, широкий угол обзора (120-180°)
- Назначение: общий обзор стеллажа, видеозапись для разрешения споров, защита от кражи/вандализма
- НЕ используется для автоматической верификации предметов (это делает телефон пользователя)

**Монтаж:**
- Камера крепится над стеллажом или на стене напротив
- Охватывает все полки одним кадром
- Запись ведётся непрерывно или по движению

**Стоимость:**

| Компонент | Кол-во | Цена за шт. | Итого |
|-----------|:------:|:-----------:|:-----:|
| IP-камера (PoE, 2-5 MP, широкий угол) | 1 | 3 000-8 000 ₽ | 3 000-8 000 ₽ |
| PoE-инжектор или адаптер | 1 | 500-1 500 ₽ | 500-1 500 ₽ |
| Крепёж, кабель | 1 комп. | 300-500 ₽ | 300-500 ₽ |
| **Итого камера** | | | **3 800-10 000 ₽** |

### 24.2 Верификация через фото с телефона пользователя

**Принцип:** Основной инструмент верификации — камера смартфона пользователя. Фото делается через приложение, которое автоматически добавляет метаданные: GPS, BLE proximity, timestamp, ID аренды.

**Защита от фальсификации:**
- GPS-координаты проверяются сервером (радиус 50 м от постамата)
- BLE proximity подтверждает присутствие рядом с постаматом
- Timestamp проверяется сервером (±5 мин от текущего времени)
- Фото делается только через приложение (нельзя загрузить из галереи)

**Фазы реализации:**

| Фаза | Фото-верификация | Обзорная камера | Подтверждение в приложении | ML-верификация фото |
|:----:|:----------------:|:---------------:|:--------------------------:|:-------------------:|
| MVP | Фото с телефона | Опционально | Чеклист + фото | Ручная (Telegram) |
| v2 | Фото с телефона | Да | Чеклист + фото | CLIP/ResNet |
| v3 | Фото с телефона (мультиракурс) | Да | Чеклист + фото | + детекция повреждений |

**Consequences:**
- Полная цепочка доказательств: загрузка → выдача → подтверждение → возврат
- Подтверждение пользователя = юридически значимый акт приёма-передачи
- Защита пользователя: может отказаться до начала аренды без последствий
- Защита бизнеса: «оно было сломано» отклоняется — есть фото + подтверждение
- MVP без ML: оператор проверяет фото в Telegram (масштабируется до ~50 аренд/день)
- Минимум оборудования на постамате: опционально 1 IP-камера вместо 16 OV5640
- Экономия ~20-55K руб./постамат (нет 16 камер, USB Hub, тензодатчиков)

**Alternatives Considered:**
- ~~16 камер OV5640 (1 на ячейку)~~ — избыточно для открытых полок, дорого
- ~~Тензодатчики HX711~~ — не применимы к открытым полкам с ремнями
- ~~UHF RFID~~ — метки срываются, переклеиваются, не определяют повреждения (см. §9)
- Только ручная проверка оператором — не масштабируется, требует выезда
- Без подтверждения пользователя — нет юридической защиты бизнеса при спорах

---

## 25. Стоимость и сроки реализации

### 25.1 Стоимость оборудования (на 1 постамат, 16 полок)

#### Фаза 1: MVP (ESP32 headless)

| Компонент | Цена (руб.) | Источник |
|-----------|:-----------:|---------|
| ESP32-S3 DevKit | 900-1 400 | AliExpress, Амперка |
| SIM7600 4G модем (UART) | 2 300-3 200 | AliExpress, LCSC |
| SIM-карта (активация) | 300-500 | МТС/Мегафон/Билайн |
| Блок питания 12V 5A | 1 000-1 800 | Чип и Дип |
| Ремни-фиксаторы (стрепы) 16 шт. | 1 600-3 200 | AliExpress, строительные магазины |
| Корпус-стеллаж (сталь 1.5-2 мм, заказной) | 18 400-36 800 | Местное производство |
| Пьезозуммер | 100-200 | Чип и Дип |
| Провода, PCB, крепёж, монтаж | 3 000-6 000 | — |
| QR-наклейка (металлизированная) | 100-300 | Типография |
| **Итого Фаза 1** | **~28 000-53 000** | |

#### Фаза 2: Продакшен (RPi + nRF52840 + ESP32 slave)

| Компонент | Цена (руб.) | Источник |
|-----------|:-----------:|---------|
| RPi CM4 (4GB/32GB eMMC) | 7 400-11 000 | Амперка, МастерКит |
| Carrier board (Pineboards/EmbeddedPi) | 2 800-7 400 | Официальные дистрибьюторы |
| nRF52840 USB Dongle (PCA10059) | 920-1 100 | Mouser, DigiKey, AliExpress |
| ESP32-S3 (slave, переиспользуется из MVP) | 900-1 400 | — |
| 4G модем (mPCIe, Quectel EC25) | 2 800-4 600 | AliExpress, LCSC |
| SIM-карта (активация) | 300-500 | — |
| Блок питания 12V 5A | 1 000-1 800 | — |
| ИБП (12V, 7Ah, 30 мин автономности) | 2 800-5 500 | Чип и Дип |
| Ремни-фиксаторы (стрепы) 16 шт. | 1 600-3 200 | AliExpress, строительные магазины |
| IP-камера обзорная (опционально) | 3 000-8 000 | — |
| Акселерометр (ADXL345, I2C) | 200-400 | AliExpress |
| Тампер-датчик (микровыключатель) | 100-300 | Чип и Дип |
| Корпус-стеллаж (сталь 2 мм, заказной) | 18 400-36 800 | Местное производство |
| Провода, PCB, крепёж, монтаж | 3 500-7 000 | — |
| QR-наклейка (металлизированная) | 100-300 | — |
| **Итого Фаза 2** | **~46 000-91 000** | |

#### Масштабирование: стоимость при серии

| Кол-во постаматов | Цена за шт. (Фаза 2) | Экономия | Примечание |
|:-:|:-:|:-:|---|
| 1 | 46-91K | — | Прототип |
| 5 | 39-77K | ~15% | Оптовые цены на корпуса/ремни |
| 10 | 35-68K | ~20-25% | Серийное производство корпусов |
| 50+ | 30-59K | ~30-35% | Прямой импорт компонентов, заказная PCB |

---

### 25.2 Ежемесячные расходы (инфраструктура и SaaS)

#### Серверная инфраструктура (VPS)

| Сервис | Цена (руб./мес.) |
|--------|:----------------:|
| VPS 2 GB RAM (Aeza, ЦОД Москва) | 593-1 000 |
| Object Storage (фото, бэкапы) | 200-500 |
| **Итого инфра** | **~800-1 500** |

> PocketBase (~200 MB) + NanoMQ (~20 MB) + Go-хуки — укладываются в 1-1.5 GB RAM на одном VPS.

#### Внешние сервисы

| Сервис | Модель оплаты | Цена (руб./мес.) |
|--------|--------------|:----------------:|
| Т-Банк СБП комиссия | 0.4% от оборота | ~200-350 * |
| CloudPayments комиссия (залоги) | 2.5-3.5% от залоговых | ~250-700 ** |
| Kit Online (фискализация) | Фиксированный | 251 |
| Zvonok.com FlashCall (OTP) | 0.25 руб./звонок | 25-75 |
| Sentry (error tracking) | Free tier / Developer | 0-2 400 |
| Домен + DNS | Годовой | 150 |
| **Итого внешние** | | **~850-3 950** |

\* *80% оборота через СБП: 80 аренд × 500 руб. × 0.4%*
\*\* *20% залоговых через CloudPayments: 20 аренд × 500 руб. × 3%*

#### Расходы на связь (на 1 постамат)

| Позиция | Цена (руб./мес.) |
|---------|:----------------:|
| IoT SIM-карта T2 M2M (трафик ~1-3 GB/мес.) | 45 |
| **Итого на постамат** | **~45** |

> Статический IP не нужен: постамат инициирует исходящее MQTT-соединение к серверу.

#### Сводка ежемесячных расходов

| Категория | 1 постамат | 5 постаматов | 10 постаматов |
|-----------|:----------:|:------------:|:-------------:|
| Аренда места в ТЦ | 15 000-40 000 | 75 000-200 000 | 150 000-400 000 |
| Ремонт и замена оборудования | 5 000-10 000 | 25 000-50 000 | 50 000-100 000 |
| VPS (Aeza) | 800-1 500 | 800-1 500 | 1 500-3 000 |
| Внешние сервисы | 850-3 950 | 2 050-8 950 | 3 600-16 900 |
| Связь (IoT SIM) | 45 | 225 | 450 |
| **Итого/мес.** | **~21 700-55 500** | **~103 100-260 700** | **~205 100-520 400** |

> **Примечание:** Аренда места и обслуживание оборудования — основные статьи расходов (~90%). VPS 2 GB обслуживает до ~100 постаматов (PocketBase + NanoMQ ~1 GB RAM). IT-расходы растут слабо. Ремонт/замена: ~10-15% стоимости оборудования в год (износ, поломки, утрата), частично покрывается залогами.

---

### 25.3 Единоразовые расходы

| Позиция | Цена (руб.) | Когда |
|---------|:-----------:|:-----:|
| **Разработка** (1 fullstack × 2 мес × 300K) | **600 000** | Месяц 1-2 |
| Оборудование для аренды (16 ед. × ~5K) | 80 000 | Месяц 1 |
| Первый постамат (Фаза 1, MVP) | 28 000-53 000 | Месяц 1 |
| Юрист: 11 документов 152-ФЗ + оферта | 30 000-80 000 | Месяц 1 |
| Т-Банк СБП подключение | 0 | Месяц 1 |
| CloudPayments подключение (залоги) | 0 | Месяц 1 |
| Kit Online подключение | 0 | Месяц 1 |
| Google Play (разовый сбор) | 2 300 ($25) | Месяц 2 |
| Apple Developer Program (годовой) | 9 200 ($99/год) | Месяц 2 |
| SSL-сертификат (Let's Encrypt) | 0 | — |
| Апгрейд до продакшена (RPi + nRF) | 18 000-38 000 * | Месяц 3-4 |
| **Итого единоразовые** | **~767 000-864 000** | |

\* *Дельта к MVP: RPi CM4+carrier (~15K) + nRF52840 (~1K) + ИБП (~4K) + IP-камера опционально (~5K) + прочее*

---

### 25.4 Сроки разработки (с ИИ-агентами)

Команда: **1 fullstack-разработчик** + ИИ-агенты (Claude Code, Copilot). Юрист — параллельный трек.

#### Фаза 1: MVP (ESP32 headless + PocketBase + Flutter) — 1.5-2 месяца

| Неделя | Задача | Компонент | Ускорение ИИ |
|:------:|--------|-----------|:------------:|
| 1 | PocketBase: коллекции (users, equipment, rentals), auth (OTP/JWT), CRUD API | Сервер | ×3 |
| 1 | Параллельно: Flutter — экраны каталога, регистрации | Мобильное | ×2.5 |
| 2 | Go-хуки: billing (Т-Банк СБП + CloudPayments auth/confirm/void), webhook handlers | Сервер | ×2.5 |
| 2 | Go-хуки: MQTT bridge (NanoMQ), locker module, Ed25519 challenge-response | Сервер | ×2.5 |
| 3 | ESP32 прошивка: BLE GATT сервер, challenge-response, телеметрия | Прошивка | ×2.5 |
| 3 | ESP32: MQTT over TLS (SIM7600), OTA, Secure Boot | Прошивка | ×2.5 |
| 4 | Flutter: BLE-интеграция (flutter_blue_plus), QR-сканер, СБП QR + CloudPayments SDK | Мобильное | ×2 |
| 4 | Flutter: экраны аренды (выбор → оплата → BLE proximity → фото → возврат) | Мобильное | ×2.5 |
| 5 | Go-хуки: фискализация (Kit Online), consent module (152-ФЗ), FlashCall OTP | Сервер | ×2.5 |
| 5 | Деплой: VPS (Aeza), PocketBase + NanoMQ, Docker Compose, CI/CD | Инфра | ×1.5 |
| 6 | Интеграция: бэкенд ↔ MQTT ↔ ESP32 ↔ BLE ↔ Flutter. E2E тестирование | Все | ×1.5 |
| 6 | Юрист: финальная проверка документов, публикация, уведомление РКН | Юрист | ×1.0 |
| 7-8 | Bug-fix, polish, тестирование на реальном постамате, запуск | Все | ×1.5 |

| Компонент | Без ИИ | С ИИ | Экономия |
|-----------|:------:|:----:|:--------:|
| PocketBase + Go-хуки (billing, MQTT, rental FSM) | 6-8 нед. | 2-2.5 нед. | 65% |
| Flutter (BLE + UI + payments) | 5-7 нед. | 2-3 нед. | 60% |
| ESP32 прошивка (BLE + MQTT + телеметрия) | 4-6 нед. | 1.5-2 нед. | 65% |
| Инфра + деплой | 1-2 нед. | 0.5-1 нед. | 50% |
| Интеграция + тестирование | 2-3 нед. | 1.5-2 нед. | 30% |
| **Итого (последовательно)** | **18-26 нед.** | **8-11 нед.** | |
| **Итого (с параллельной работой)** | **12-16 нед.** | **6-8 нед. (1.5-2 мес.)** | **~50%** |

> Параллелизация: PocketBase + Go-хуки и Flutter разрабатываются одновременно (один разработчик переключается). ESP32 — после определения API (неделя 3).

#### Фаза 2: Продакшен (RPi + nRF) — 1-1.5 месяца

| Неделя | Задача | Ускорение ИИ |
|:------:|--------|:------------:|
| 1 | Go Core Service на RPi: портирование, SQLite WAL, BlueZ BLE (nRF52840) | ×2.5 |
| 1 | ESP32 slave: убрать BLE/MQTT, добавить UART протокол с RPi | ×2.5 |
| 2 | Опциональная IP-камера, Ansible playbooks, fleet management | ×2 |
| 3 | Prometheus + Grafana на YC, Sentry интеграция, алерты | ×2 |
| 3-4 | Интеграция + Hardware-in-the-loop тестирование, stress test | ×1.5 |

#### Суммарный таймлайн

| Фаза | Срок | Результат |
|------|:----:|-----------|
| **Фаза 1: MVP** | **1.5-2 мес.** | 1 постамат, ESP32, BLE, Flutter, PocketBase, Т-Банк СБП, Kit Online |
| **Фаза 2: Продакшен** | **1-1.5 мес.** | RPi CM4, nRF52840, offline, fleet management |
| **Всего до продакшена** | **2.5-3.5 мес.** | Готовая система для масштабирования |
| Юрист (параллельно) | 3-4 нед. | 11 документов 152-ФЗ, оферта, уведомление РКН |

---

### 25.5 Финансовая модель (первый год)

#### Сценарий: 1 постамат, 16 полок, средний чек 500 руб., 100 аренд/мес.

| Категория | Сумма (руб.) |
|-----------|:------------:|
| **Единоразовые расходы** | |
| Разработка (1 fullstack × 2 мес × 300K) | 600 000 |
| Оборудование для аренды (16 ед. × ~5K) | 80 000 |
| Стеллаж MVP (Фаза 1) | 40 000 |
| Апгрейд до продакшена (Фаза 2) | 28 000 |
| Юрист + подключения + инструменты | 30 000 |
| Сторы (Google + Apple) | 11 500 |
| **Итого единоразовые** | **~790 000** |
| | |
| **Ежемесячные расходы (×12)** | |
| Аренда места в ТЦ | 15 000 × 12 = 180 000 |
| Ремонт и замена оборудования | 5 000 × 12 = 60 000 |
| VPS (Aeza) | 800 × 12 = 9 600 |
| Kit Online (фискализация) | 251 × 12 = 3 012 |
| Zvonok.com FlashCall | 50 × 12 = 600 |
| Т-Банк СБП комиссия (0.4% × 40K) | 160 × 12 = 1 920 |
| CloudPayments комиссия (3% × 10K залоговых) | 300 × 12 = 3 600 |
| IoT SIM (T2 M2M) | 45 × 12 = 540 |
| **Итого ежемесячные за год** | **~259 272** |
| | |
| **ИТОГО за первый год** | **~1 049 000** |
| | |
| **Выручка** (100 аренд × 500 руб. × 12 мес.) | **600 000** |
| **Маржа (1 постамат, 1й год)** | **−449 000 (убыток)** |
| | |
| **При 5 постаматах** | |
| Инвестиции (разработка 600K + 5 × (стеллаж 20K + оборудование 80K) + прочее 70K) | ~1 170 000 |
| Ежемесячная прибыль (5 × 28 284) | ~141 000 |
| Окупаемость (с рэмп-апом) | **~10 мес.** |

#### Точка безубыточности

| Метрика | Значение |
|---------|:--------:|
| Единоразовые инвестиции (вкл. разработку 600K, оборудование 80K) | ~790 000 руб. |
| Ежемесячные расходы (место 15K + ремонт 5K + IT ~1.7K) | ~21 716 руб. |
| Средний чек | 500 руб. |
| Маржа с аренды (после комиссий) | ~493 руб. |
| Аренд для покрытия ежемесячных | ~44 аренды/мес. |
| Окупаемость 1 постамат (100 аренд/мес) | ~26 мес. |
| Окупаемость 5 постаматов (~1.17M инвестиций, прибыль ~141K/мес) | ~10 мес. (с рэмп-апом) |

> **Важно:** Разработка — основная статья единоразовых расходов (600K из 790K), при масштабировании «размазывается». Аренда места в ТЦ + обслуживание оборудования — основные ежемесячные расходы (~92%). Ремонт/замена частично покрывается залогами арендаторов.

---

## 26. MQTT-протокол: регистрация постамата и управление полками

### 26.1 Общая архитектура MQTT

```
┌──────────────┐      mTLS (port 8883)      ┌──────────────┐       ┌──────────────┐
│  Постамат    │◄──────────────────────────►│  NanoMQ        │◄─────►│  Go Backend  │
│  (RPi CM4)   │   per-device X.509 cert    │  MQTT Broker │  TCP  │  (subscriber │
│              │                            │  + ACL       │       │   + publisher)│
└──────────────┘                            └──────────────┘       └──────────────┘
```

**Брокер:** NanoMQ (self-hosted, Docker Compose на VPS).
**Транспорт:** MQTT 5.0 over TLS 1.3 (port 8883). Plaintext (1883) отключён.
**Аутентификация:** mTLS — каждый постамат имеет уникальный X.509 клиентский сертификат.

### 26.2 Factory Provisioning (подготовка на производстве)

При сборке постамата на каждое устройство записываются:

| Артефакт | Хранение | Назначение |
|----------|----------|------------|
| CA root certificate | `/etc/mqtt/ca.pem` | Верификация сервера (NanoMQ) |
| Device client cert | `/etc/mqtt/device.pem` | Аутентификация устройства (CN = serial number) |
| Device private key | `/etc/mqtt/device.key` | Подпись TLS handshake (chmod 600) |
| Ed25519 public key (сервера) | `/etc/ble/server_pub.pem` | Верификация BLE Challenge-Response |
| Serial number | `/etc/device/serial` | Уникальный идентификатор постамата |

**Процесс:**
1. Генерация CSR на устройстве (приватный ключ не покидает устройство).
2. Подпись CSR на PKI-сервере → device.pem (CN = `LKR-{serial}`).
3. Запись ca.pem, device.pem, server_pub.pem на eMMC/SD.
4. ESP32: Secure Boot v2 + Flash Encryption включены.

### 26.3 First Boot — регистрация постамата

```
Постамат                          NanoMQ                         Go Backend
    │                               │                               │
    │  1. TLS handshake (mTLS)      │                               │
    │  ─────────────────────────►   │                               │
    │  CN=LKR-ABC123                │                               │
    │                               │  ACL: LKR-ABC123 может       │
    │                               │  писать locker/ABC123/*       │
    │                               │                               │
    │  2. PUBLISH                   │                               │
    │  locker/ABC123/register       │  ──────────────────────────►  │
    │  QoS 1                        │                               │
    │  {                            │                               │
    │    "serial": "ABC123",        │                               │
    │    "hw_version": "2.1",       │                               │
    │    "fw_version": "1.0.0",     │                               │
    │    "slots_count": 16,          │                               │
    │    "capabilities": [          │                               │
    │      "ble", "telemetry"       │                               │
    │    ],                         │                               │
    │    "ip": "10.0.1.42",         │                               │
    │    "uptime": 0                │                               │
    │  }                            │                               │
    │                               │                               │
    │                               │  3. Backend создаёт запись    │
    │                               │  в БД (status=pending)        │
    │                               │                               │
    │                               │  4. PUBLISH                   │
    │  ◄────────────────────────────│  locker/ABC123/config         │
    │                               │  QoS 1, retained              │
    │  {                            │                               │
    │    "locker_id": "uuid-...",   │                               │
    │    "heartbeat_interval": 30,  │                               │
    │    "telemetry_interval": 60,  │                               │
    │    "slots": [                 │                               │
    │      {"index": 0, "size": "M",│                              │
    │       "enabled": true},       │                               │
    │      ...                      │                               │
    │    ]                          │                               │
    │  }                            │                               │
    │                               │                               │
    │  5. Постамат применяет конфиг │                               │
    │  и начинает heartbeat         │                               │
    │                               │                               │
    │  6. Оператор активирует       │                               │
    │  постамат в админке           │  ──────── status=active ────► │
    │  (указывает адрес, название)  │                               │
    └───────────────────────────────┘───────────────────────────────┘
```

**Статусы постамата:** `pending` → `active` → `maintenance` / `disabled`.
Оператор подтверждает регистрацию в админке (ADM-02) — до этого постамат не принимает аренды.

### 26.4 Структура MQTT-топиков

| Топик | Направление | QoS | Retained | Описание |
|-------|:-----------:|:---:|:--------:|----------|
| `locker/{id}/register` | Device → Server | 1 | Нет | Регистрация при первом подключении |
| `locker/{id}/heartbeat` | Device → Server | 0 | Нет | Пульс (каждые 30 сек) |
| `locker/{id}/telemetry` | Device → Server | 0 | Нет | Телеметрия (каждые 60 сек) |
| `locker/{id}/status` | Device → Server | 1 | Да | Общий статус постамата |
| `locker/{id}/status/slot/{n}` | Device → Server | 1 | Да | Статус конкретной полки |
| `locker/{id}/event` | Device → Server | 1 | Нет | События (выдача, возврат, тампер) |
| `locker/{id}/photo` | Server → Device | 1 | Нет | Уведомление о загруженном фото (фото загружается пользователем через REST API) |
| `locker/{id}/cmd/slot` | Server → Device | 1 | Нет | Команды управления полкой |
| `locker/{id}/cmd/system` | Server → Device | 1 | Нет | Системные команды (reboot, diagnostics) |
| `locker/{id}/config` | Server → Device | 1 | Да | Конфигурация постамата |
| `locker/{id}/ota` | Server → Device | 1 | Нет | Команда OTA-обновления |

**Last Will and Testament (LWT):**
- Топик: `locker/{id}/status`
- Payload: `{"online": false, "last_seen": "<timestamp>"}`
- QoS: 1, Retained: Да

### 26.5 Управление полками — команды и статусы

#### Команды (`locker/{id}/cmd/slot`)

```json
// Назначение оборудования на полку
{
  "cmd": "assign_equipment",
  "slot_index": 3,
  "request_id": "uuid-...",
  "equipment_id": "uuid-...",
  "equipment_name": "Перфоратор Bosch GBH 2-26",
  "timestamp": "2026-03-26T10:30:00Z"
}

// Снятие оборудования с полки
{
  "cmd": "unassign_equipment",
  "slot_index": 3,
  "request_id": "uuid-...",
  "timestamp": "2026-03-26T10:30:00Z"
}

// Блокировка полки (обслуживание)
{
  "cmd": "disable",
  "slot_index": 3,
  "request_id": "uuid-...",
  "reason": "maintenance",
  "timestamp": "2026-03-26T10:30:00Z"
}

// Инвентаризация — запросить текущие данные всех полок
{
  "cmd": "inventory",
  "request_id": "uuid-...",
  "timestamp": "2026-03-26T10:30:00Z"
}
```

#### Ответы (`locker/{id}/event`)

```json
// Подтверждение выполнения команды
{
  "event": "cmd_ack",
  "request_id": "uuid-...",
  "slot_index": 3,
  "status": "ok",              // ok | error
  "error": null,               // "slot_not_found" | ...
  "timestamp": "2026-03-26T10:30:01Z"
}

// Оборудование выдано (подтверждено через приложение)
{
  "event": "equipment_picked_up",
  "slot_index": 3,
  "rental_id": "uuid-...",
  "photo_id": "uuid-...",       // ссылка на фото с телефона пользователя
  "timestamp": "2026-03-26T10:30:02Z"
}

// Оборудование возвращено (подтверждено через приложение)
{
  "event": "equipment_returned",
  "slot_index": 3,
  "rental_id": "uuid-...",
  "photo_id": "uuid-...",       // ссылка на фото с телефона пользователя
  "verification": "pending",    // pending | pass | fail
  "timestamp": "2026-03-26T10:31:15Z"
}

// Тампер (попытка взлома/вандализм)
{
  "event": "tamper",
  "slot_index": 3,
  "type": "vibration",          // vibration | power_loss | unauthorized_removal
  "timestamp": "2026-03-26T10:30:05Z"
}
```

#### Статус полки (`locker/{id}/status/slot/{n}`, retained)

```json
{
  "slot_index": 3,
  "occupied": true,             // есть оборудование на полке
  "equipment_id": "uuid-...",
  "equipment_name": "Перфоратор Bosch GBH 2-26",
  "enabled": true,              // false = заблокирована для обслуживания
  "last_event": "equipment_returned",
  "updated_at": "2026-03-26T10:31:15Z"
}
```

### 26.6 Телеметрия и Heartbeat

#### Heartbeat (`locker/{id}/heartbeat`, QoS 0)

```json
{
  "uptime_sec": 86400,
  "free_ram_mb": 512,
  "cpu_temp_c": 45.2,
  "fw_version": "1.0.3",
  "mqtt_reconnects": 0,
  "ble_connections_active": 1,
  "timestamp": "2026-03-26T10:30:00Z"
}
```

#### Телеметрия (`locker/{id}/telemetry`, QoS 0)

```json
{
  "temperature_c": 22.5,
  "humidity_pct": 45,
  "voltage_v": 12.1,
  "signal_rssi_dbm": -67,
  "slots_summary": {
    "total": 16,
    "occupied": 11,
    "available": 3,
    "disabled": 2
  },
  "storage_used_pct": 12,
  "timestamp": "2026-03-26T10:30:00Z"
}
```

### 26.7 Загрузка фото

Фото не передаются через MQTT (ограничение размера payload). Схема:

Фото возврата делает пользователь через камеру телефона и загружает через приложение:

1. Пользователь фиксирует оборудование на полке ремнём.
2. Нажимает «Сфотографировать возврат» в приложении → камера телефона.
3. Приложение отправляет фото на бэкенд: `POST /api/rentals/{rental_id}/photo` (multipart/form-data).
4. Бэкенд сохраняет фото в S3 (Yandex Object Storage), привязывает URL к записи аренды.
5. Оператор верифицирует фото (P1) или AI-модель проверяет автоматически (P2).

```json
// MQTT-уведомление бэкенда → постамат (обновление статуса полки)
// Topic: locker/{id}/cmd/slot
{
  "cmd": "update_status",
  "slot_index": 3,
  "status": "occupied",
  "rental_id": "uuid-...",
  "timestamp": "2026-03-26T10:31:15Z"
}
```

### 26.8 OTA-обновление

```json
// Команда обновления (locker/{id}/ota)
{
  "cmd": "update",
  "component": "edge",          // edge | esp32 | nrf52840
  "version": "1.1.0",
  "url": "https://storage.yandexcloud.net/rent-ota/edge-1.1.0.tar.gz",
  "sha256": "a1b2c3d4...",
  "size_bytes": 15000000,
  "force": false,               // true = обновить немедленно, false = при простое
  "timestamp": "2026-03-26T02:00:00Z"
}

// Ответ (locker/{id}/event)
{
  "event": "ota_progress",
  "component": "edge",
  "version": "1.1.0",
  "status": "downloading",      // downloading | verifying | installing | rebooting | done | error
  "progress_pct": 45,
  "error": null,
  "timestamp": "2026-03-26T02:01:30Z"
}
```

---

## 27. Безопасность MQTT: mTLS, ACL, ротация сертификатов

### 27.1 Модель угроз

| Угроза | Вектор | Последствие | Защита |
|--------|--------|-------------|--------|
| Перехват трафика | Man-in-the-Middle | Утечка команд, телеметрии | TLS 1.3 (шифрование канала) |
| Подмена постамата | Фальшивое устройство подключается к брокеру | Ложные данные, кража аренд | mTLS (клиентский сертификат) |
| Подмена сервера | DNS spoofing, ARP poisoning | Постамат подключается к злоумышленнику | Certificate pinning (CA root) |
| Горизонтальное движение | Скомпрометированный постамат управляет другими | Ложные данные о других постаматах | ACL (изоляция топиков) |
| Кража ключей с устройства | Физический доступ к eMMC/SD | Клонирование устройства | Secure Boot, Flash Encryption, тампер-датчик |
| Кража с открытой полки | Физический доступ к стеллажу | Утрата оборудования | Обзорная IP-камера, BLE proximity, размещение в охраняемых зонах |
| Подмена предмета | Замена оборудования на похожий/сломанный предмет | Получение повреждённого оборудования следующим арендатором | Фото-верификация при выдаче и возврате, ML-сравнение с эталоном |
| Relay-атака (BLE) | Ретрансляция BLE proximity на расстоянии | Ложное подтверждение присутствия | Challenge-Response с nonce + timestamp + GPS (§23) |
| Просроченный сертификат | Сертификат истёк, постамат не подключается | Постамат offline | Автоматическая ротация до истечения |

### 27.2 mTLS — взаимная аутентификация

#### Инфраструктура PKI

```
┌─────────────────┐
│  Root CA        │  Самоподписанный, offline (air-gapped)
│  (ed25519)      │  Срок: 10 лет
└────────┬────────┘
         │ подписывает
    ┌────┴────┐
    │         │
┌───┴───┐ ┌──┴────┐
│Issuing│ │Server │
│  CA   │ │ Cert  │
│(online)│ │(NanoMQ) │
│3 года │ │1 год  │
└───┬───┘ └───────┘
    │ подписывает
    │
┌───┴──────────┐
│Device Certs  │
│(per-locker)  │
│CN=LKR-{serial}│
│Срок: 2 года  │
└──────────────┘
```

#### Конфигурация NanoMQ

```hocon
# nanomq.conf — фрагмент
listeners.ssl {
  bind = "0.0.0.0:8883"
  tls {
    cacertfile = "/etc/nanomq/certs/ca-chain.pem"
    certfile   = "/etc/nanomq/certs/server.pem"
    keyfile    = "/etc/nanomq/certs/server.key"
    verify_peer = true
    fail_if_no_peer_cert = true
  }
}
```

#### Подключение постамата

```
Постамат (RPi)                                 NanoMQ
     │                                           │
     │  ClientHello (TLS 1.3)                    │
     │  ─────────────────────────────────────►   │
     │                                           │
     │  ServerHello + ServerCert + CertRequest   │
     │  ◄─────────────────────────────────────   │
     │                                           │
     │  ClientCert (CN=LKR-ABC123) + Verify      │
     │  ─────────────────────────────────────►   │
     │                                           │
     │  NanoMQ верифицирует:                       │
     │  1. Цепочка: Device → Issuing CA → Root   │
     │  2. CN → username для ACL                 │
     │  3. Не отозван (CRL / OCSP)               │
     │                                           │
     │  Finished (TLS handshake complete)         │
     │  ◄─────────────────────────────────────   │
     │                                           │
     │  MQTT CONNECT (username = CN)              │
     │  ─────────────────────────────────────►   │
     └───────────────────────────────────────────┘
```

### 27.3 ACL — изоляция топиков

Каждый постамат может читать/писать **только** свои топики. NanoMQ ACL-правила:

```hocon
# nanomq_acl.conf
authorization {
  no_match = deny          # По умолчанию — запрет
  deny_action = disconnect  # При нарушении — отключение
  sources = [
    {
      type = file
      enable = true
      path = "/etc/nanomq/acl.conf"
    }
  ]
}
```

#### Правила доступа

| Клиент (CN) | Действие | Топик | Разрешение |
|-------------|----------|-------|:----------:|
| `LKR-{id}` | publish | `locker/{id}/register` | ✅ |
| `LKR-{id}` | publish | `locker/{id}/heartbeat` | ✅ |
| `LKR-{id}` | publish | `locker/{id}/telemetry` | ✅ |
| `LKR-{id}` | publish | `locker/{id}/status/#` | ✅ |
| `LKR-{id}` | publish | `locker/{id}/event` | ✅ |
| `LKR-{id}` | subscribe | `locker/{id}/photo` | ✅ |
| `LKR-{id}` | subscribe | `locker/{id}/cmd/#` | ✅ |
| `LKR-{id}` | subscribe | `locker/{id}/config` | ✅ |
| `LKR-{id}` | subscribe | `locker/{id}/ota` | ✅ |
| `LKR-{id}` | publish/subscribe | `locker/{other_id}/#` | ❌ |
| `LKR-{id}` | subscribe | `locker/+/#` (wildcard) | ❌ |
| `backend` | publish/subscribe | `locker/#` | ✅ (все топики) |
| `admin` | subscribe | `locker/#` | ✅ (read-only мониторинг) |

**Ключевой принцип:** Даже если злоумышленник получит сертификат одного постамата, он не сможет управлять другими.

### 27.4 Ротация сертификатов

Сертификаты устройств имеют срок 2 года. Ротация начинается за 30 дней до истечения.

```
Go Backend                           NanoMQ                          Постамат
    │                                  │                               │
    │  1. Cron: проверка сроков        │                               │
    │  (ежедневно, за 30 дней)         │                               │
    │                                  │                               │
    │  2. Генерация нового cert        │                               │
    │  (Issuing CA подписывает)        │                               │
    │                                  │                               │
    │  3. PUBLISH                      │                               │
    │  locker/{id}/cmd/system          │  ─────────────────────────►  │
    │  {                               │                               │
    │    "cmd": "rotate_cert",         │                               │
    │    "new_cert": "-----BEGIN...",   │                               │
    │    "new_key_encrypted": "...",    │  (зашифрован текущим         │
    │    "valid_from": "2028-03-01",   │   device key)                 │
    │    "valid_until": "2030-03-01"   │                               │
    │  }                               │                               │
    │                                  │                               │
    │                                  │  4. Постамат:                 │
    │                                  │  - расшифровывает new_key      │
    │                                  │  - сохраняет new cert/key     │
    │                                  │  - переподключается           │
    │                                  │                               │
    │  5. NanoMQ: обновляет ACL          │                               │
    │  для нового CN (если изменился)  │                               │
    │                                  │                               │
    │  6. Старый cert → CRL            │                               │
    │  (Certificate Revocation List)   │                               │
    └──────────────────────────────────┘───────────────────────────────┘
```

#### Экстренный отзыв (компрометация)

При подозрении на компрометацию устройства:
1. Оператор нажимает «Отключить постамат» в админке.
2. Backend добавляет сертификат в CRL (NanoMQ перезагружает CRL каждые 5 мин).
3. NanoMQ разрывает соединение при следующем heartbeat.
4. Постамат не может переподключиться (CRL check fails).
5. Для восстановления — физический re-provisioning (новый сертификат на месте).

### 27.5 Защита payload

| Уровень | Механизм | Что защищает |
|---------|----------|-------------|
| Транспорт | TLS 1.3 | Шифрование + целостность всего трафика |
| Аутентификация | mTLS (X.509) | Идентификация устройства и сервера |
| Авторизация | NanoMQ ACL | Изоляция топиков между устройствами |
| BLE proximity | Ed25519 Challenge-Response | Подлинность proximity-подтверждения (§23) |
| OTA-прошивки | SHA-256 хеш | Целостность файла обновления |
| Ключи на устройстве | Secure Boot + Flash Encryption | Защита от чтения/модификации прошивки |
| Физический доступ | Тампер-датчик + алерт | Обнаружение вскрытия корпуса |

> **Дополнительное шифрование payload (AES) не требуется** — TLS 1.3 обеспечивает шифрование на транспортном уровне. Добавление второго слоя шифрования увеличит нагрузку на ESP32 без значимого прироста безопасности.

### 27.6 Мониторинг безопасности

| Событие | Источник | Алерт |
|---------|----------|-------|
| Неудачная TLS-аутентификация | NanoMQ logs | Telegram (немедленно) |
| ACL-нарушение (попытка доступа к чужому топику) | NanoMQ logs | Telegram (немедленно) |
| Тампер-датчик сработал | `locker/{id}/event` | Telegram + SMS оператору |
| Сертификат истекает <7 дней | Backend cron | Email оператору |
| Постамат offline >10 мин | Missing heartbeat | Telegram |
| Множественные BLE proximity failure | `locker/{id}/event` | Telegram (возможная атака) |
| OTA-обновление failed | `locker/{id}/event` | Telegram |
