# Домен L — Облако / компаньон > Апсайд-слой поверх локальной системы: мобильное приложение-компаньон, синхронизация/бэкап, > **OTA-канал** (сборка/хостинг/доставка) и **opt-in телеметрия**. **Облако опционально и > self-hostable** — система полноценна без него (vision: открыта в любом случае, #3). Никогда не > управляет машиной. Статус: **v2 (на ревью).** v2 — после adversarial-ревью (21 находка). Связано с: [a-base-system.md](a-base-system.md) (§5 OTA-механизм/подпись/anti-rollback, §3 fscrypt, §7 время/TLS-gate, §9 audit, §12 factory-reset) · [security-privacy.md](../contracts/security-privacy.md) (§5 грант, §7 телеметрия/152-ФЗ) · [ipc.md](../contracts/ipc.md) (`Settings`, `Power`, `Connectivity`) · [architecture.md](../architecture.md) (§3 Companion first-party, §5 карта связей) · домены **G** (Connectivity-транспорт: `State`/metered/portal), **D** (§7 память водителя — синк), **E** (live-данные/DTC — read-only), **F**/plugin-sdk (§8 trip-плагин — поездки), **J** (§4 dashcam-гейт), **B** (§4/§7 lifecycle OTA), **C** (consent/UI) · [principles.md](../principles.md) (#1,#2,#3,#7,#8,#12,#13) --- ## 1. Назначение и границы - **Что делает:** (1) **компаньон** — моб.приложение, спаренное с головблоком (просмотр данных машины/DTC и поездок, просмотр/правка памяти водителя, настройки, уведомления); (2) **синхронизация/бэкап** `/data`; (3) **OTA-канал** — сборка/хостинг/доставка подписанных образов (применяет/верифицирует A); (4) **телеметрия** — opt-in, выключена по умолчанию. - **Облако опционально и self-hostable (#3):** все ключевые функции работают **локально device↔телефон** (по близости, без облака); облако — лишь relay/хостинг для удалённого и OTA. Нет облака — система полноценна. - **Границы (красные линии):** - **Никогда не управляет машиной (#1/#2):** компаньон/облако — **read-only** к данным машины (через головблок, сам read-only к CAN); удалённых actuator-команд нет, write-путь не существует. - **Приватность (#7/152-ФЗ):** телеметрия opt-in; память водителя / контакты / точная локация / **dashcam-медиа** — **не в облако без явного отзываемого согласия** (security-privacy §7, J §4); данные в РФ; облако — RU/self-hosted. - **L — «первый продукт»/ретрофит-слой** (vision-лестница): созревает поздно (v3–v4), апсайд, не ядро. - **Plane:** control/синк/телеметрия — поверх IP (Connectivity, G); локально device↔телефон — лок.сеть/BT. ## 2. Архитектура L (три части, local-first) - **Device-side — `Companion`-сервис** (first-party ап на SDK, головблок; architecture §3): локальный API для моб.приложения, координирует синк (Settings/State + память D §7), **триггерит OTA** (применяет A §5), собирает opt-in телеметрию. Поднимается на **Stage 2** (фоном, как Connectivity; architecture §6) — boot-окно-поверхности нет. - **Моб.приложение** (off-device артефакт): iOS/Android; стек — 🟡 (§3). - **Опциональное облако** (self-hostable, RU): relay синка (когда нет близости), **хостинг OTA-бандлов** (untrusted — A верифицирует подпись + anti-rollback, §7), приём opt-in телеметрии. Provider-agnostic (#8). - **Local-first:** по близости device↔телефон общаются напрямую (лок.WiFi/BT), облако не нужно. ## 3. Companion-приложение (моб.) - **Функции:** просмотр **данных машины/DTC** (read-only, из E) и **поездок** (из storage trip-плагина, plugin-sdk §8 — не из core E), **просмотр/правка/чистка памяти водителя** (`.md`, D §7), управление настройками (зеркало Settings), уведомления (§5), статус OTA. - **Стек 🟡:** моб.приложение **off-device**, поэтому Rust-first-правило прода (tech-stack — про головблок) на него не распространяется. Кандидаты: Flutter (один кодбейс) **или** Rust-core + native-UI. MIT (#12). → §15. - **Фаза:** v3. ## 4. Паринг и доверие - **Локальный якорь доверия:** головблок показывает pairing-код/QR → ввод/скан в приложении → **per-peer общий ключ** (BT/Tailscale-стиль). **Аккаунт в облаке не обязателен** для локального паринга/синка. - Спаренный телефон — доверенный peer; список спаренных, **отзыв — в Companion-сервисе** (с головблока, без телефона; слот C §4). **Модель ролей между несколькими peer'ами** (владелец vs гость: что гость видит/правит, особенно память водителя) и **адресация N головблоков одним аккаунтом** — открыты (§15). - **Удалённый доступ (через облако-relay)** — опц., требует аккаунта; E2E-шифрование канала (trust-якорь — тот же pairing-ключ); account-recovery — §15. **Фаза:** v3 (локально) / v4 (удалённо). ## 5. Синхронизация, уведомления, удалённый статус - **Что синкается:** настройки (Settings namespace), **память водителя** (`.md`, D §7), история поездок (trip-плагин, app-storage §6). **Local-first**, device↔телефон по близости; облако-relay — опц. для удалённого. - **Конфликты:** per-key LWW для настроек; для `.md`-памяти — merge/LWW/версионирование (выбор — §15). - **Что НЕ синкается без явного согласия:** память водителя в **облако** (D §7), контакты/журнал (G §5), **dashcam-медиа** (J §4), точная локация. Согласие — отзываемое (security-privacy §7), UI — C. - **Уведомления:** локально/по близости — прямой канал device↔телефон (in-app, сигналы §11). Удалённо (телефон не рядом / приложение в фоне) — через self-hosted relay (long-poll/WebSocket) **либо** честное ограничение «без облака удалённых push нет». **APNs/FCM** — внешние зависимости, конфликт с self-hostable/152-ФЗ (#8) → не first-party (🟡 §15). Фаза: v3 локально / v4 удалённо. - **Egress-гейт связности:** синк/relay-egress стартует только при Connectivity `State==online` (G §11), **не на `portal`/`limited`** (ложный online — captive-portal, G §2) и **не до «вменяемых часов»** (§9). - **Удалённый статус машины (read-only, later):** «где машина / напряжение / есть ли DTC» — только данные, которые головблок уже имеет (никакого нового доступа к CAN, #2); **локация в облако — opt-in + consent**; свежесть/last-known vs live + порог согласия — §15. Фаза: later/v4. - **Фаза:** синк v3; уведомления v3/v4; удалённый статус v4. ## 6. Бэкап / восстановление - **Бэкап `/data`** (настройки, память `.md`, app-storage); опц. — **отдельный поток dashcam-медиа** (на отдельном носителе/разделе, J §4 / A §12 — НЕ в `/data`): только по capability + **явному отзываемому согласию**, факт — в audit-log (J §4 / security-privacy §7); по умолчанию нет. - **Шифрование бэкапа:** fscrypt-ключ `/data` привязан к **eFuse конкретной платы и headless** (a-base §3) — у пользователя его НЕТ и он непереносим. Поэтому: (а) restore **на том же устройстве** (после factory-reset) — ок; (б) **cross-device restore** требует ОТДЕЛЬНОГО backup-ключа/парольной фразы (не из eFuse) + re-encrypt при экспорте — **схема открыта (§15, расширить a-base §3)**; до резолва cross-device-restore зашифрованного не гарантируется. - **Восстановление:** настройки/память на новом устройстве/после factory-reset; **device-identity/`machine-id` не переносится** (a-base §11). **Совместимость:** бэкап несёт версию base-образа/схемы Settings/BSP-профиля; при несовпадении BSP/vehicle-профиля (a-base §11/§13) BSP-зависимые ключи **не применяются молча** (видимая индикация / миграция схемы), переносится BSP-независимое (память `.md` — безопасна; расхождение сигналов штатно покрыто `Unavailable`, data-model §3). Детали миграции — §15. - **Фаза:** v3/v4. ## 7. OTA-канал *(L владеет доставкой; A — применением)* - **Шов с A (a-base §5):** **A** — механизм применения (RAUC A/B), подпись/keyring/верификация + **anti-rollback**, откат (bootcount+mark-good). **L** — **сборка** (CI → подписанный бандл + метаданные min-version), **хостинг** (сервер/CDN), **доставка** (device-side update-клиент: проверка/скачивание). - **Untrusted-host (с anti-rollback):** бандл подписан ключом из OTP/eFuse (a-base §4) → подмену вредоноса хост не протолкнёт. Но подпись гарантирует аутентичность/целостность, **НЕ свежесть** → A дополнительно проверяет **монотонность security-version** (rollback-index в eFuse/U-Boot env, по модели AVB) и отвергает downgrade/replay даже при валидной подписи. **Только с anti-rollback** тезис «хосту не нужно доверие, лишь TLS+доступность» корректен — иначе скомпрометированный CDN откатит парк на подписанную дырявую прошивку. - **Фазы DOWNLOAD (L) vs APPLY (A) — разведены:** скачивание (L) идёт в **staging-область**, ДО RAUC, и **не трогает A/B-слоты**; применение (A) — атомарно (a-base §5). Прерывание питанием на DOWNLOAD теряет максимум прогресс докачки (слоты целы); на APPLY — RAUC откат. - **Отказ-пути доставки (симметрично J §4 / H §5):** проверка свободного места ДО скачивания (ENOSPC → отказ с видимой индикацией, без частичной записи, не крэш); прерванная загрузка — **range-resume** или чистый перезапуск; **обрезанный/битый бандл отвергается на стороне L** (ожидаемый размер+хэш из подписанного манифеста) ДО передачи в RAUC, не полагаясь на подпись A как единственный backstop; недоступный CDN → отложить (#3). - **Связность:** скачивание — только при `State==online` (G §11), не на `portal`/`limited` (G §2); metered-aware (крупный бандл не по дорогой LTE без согласия, G §2); и не до «вменяемых часов» (§9, иначе TLS «not yet valid»). - **Каналы релизов:** stable / beta (opt-in); **staged rollout** (% парка), delta — позже (§15). - **Сигнал здоровья когорты + auto-halt:** staged rollout требует обратного канала о доле mark-good/откатов; при пороге откатов — автоостановка раскатки на остальной парк. **Парадокс:** телеметрия (§8) opt-in и по умолчанию молчит → единственный обратный канал ненадёжен. Резолв — §15 (мин. неотключаемый health-сигнал результата OTA vs консервативная ручная раскатка). Без него плохой бандл (откатившийся локально, A) идёт дальше. - **До secure boot (v0–v3):** trust-by-build / ручной флеш (security-privacy §5); полноценный подписанный OTA-канал + anti-rollback созревают с secure boot — **v4** (a-base §2/§4/§5). - **Фаза:** v4. ## 8. Телеметрия *(opt-in, выключена по умолчанию)* - **По умолчанию ВЫКЛЮЧЕНА** (security-privacy §7, #7); включается явным **отзываемым** согласием. - **Что собирается (при opt-in):** краши/паники, результат OTA, watchdog-ресеты (источник — критичные события a-base §9), агрегатная статистика использования (какие функции, без контента), perf/ошибки. - **Что НИКОГДА (без отдельного явного согласия):** голос, контент данных машины, контакты, память водителя, точная локация, dashcam. - **Граница согласия:** **не ретроспективно** (собираем только события ПОСЛЕ согласия; ранее осевшие в audit-log/ pstore критичные события a-base §9 в egress не идут); **отзыв** → немедленно прекратить сбор + **дропнуть локальный буфер** ещё-не-отправленного (строгая трактовка #7); системный audit-log/pstore (a-base §9) ведётся независимо для recovery, телеметрии не равен. - **Factory-reset (a-base §12):** стирает telemetry-consent (возврат к выкл-по-умолчанию #7) И буфер несобранной/неотправленной телеметрии (мог содержать события прошлого владельца — кейс продажи авто). **Инвариант:** после factory-reset **все opt-in-согласия = выкл** (синк памяти, локация — тоже). - **Облачная сторона:** ingest имеет retention; **отзыв согласия → удаление в ingest**; factory-reset стирает только локальное — облачная копия удаляется отдельным запросом (право 152-ФЗ; self-hosted — у владельца инстанса). Детали — §15. - **Анонимизация** (без device-identity/VIN/локации), **RU/self-hosted** ingest, provider-agnostic (#8); факт egress — в audit-log (security-privacy §7 / a-base §9). - **Фаза:** v2/v3. ## 9. Lifecycle (шов с B) - **OTA-тайминг:** применение (A) — **не на ходу**; предпочтительно на стоянке/`accessory` или по явному запросу (Companion проверяет `Power`-тайминг, делегирует A); скачивание — фоном, metered/portal/online-aware (§7). - **Time-gate (a-base §7 / B §8):** OTA-download, sync-relay и telemetry-egress подчиняются гейту «вменяемых часов» — откладываются до валидного времени (fake-hwclock/NTP/GPS-фикс), иначе TLS к CDN/облаку/ingest падает «cert not yet valid» (плата без RTC → холодный boot с epoch 1970). Без сети ≠ без валидного времени. - **Sync/телеметрия:** фоновые, неблокирующие (#11); деградируют без сети (#3); **не инициируют/не продлевают wake** — согласуется с энергобюджетом АКБ (B §7 battery-cutoff, #5). - **На `ShutdownImminent` (B §4):** прервать фоновую загрузку/синк gracefully (staging цел, §7); доделать позже. ## 10. Приватность и безопасность (#1, #2, #7, #12) - **Read-only к машине, без удалённого управления** (#1/#2): write-путь к CAN не существует ни локально, ни через облако. - **Local-first, облако опционально**: память/контакты/локация/**dashcam** не в облако без явного согласия (D §7, G §5, J §4, security-privacy §7). - **Untrusted OTA-host** (§7): доверие — в подписи **+ anti-rollback** (A/eFuse), не в хосте. - **152-ФЗ:** телеметрия opt-in, данные в РФ, **право на экспорт/удаление из облака** (синк + egress-нувшая телеметрия), оператор/основание ПДн — legal-трек; self-hostable снимает зависимость от чужого облака. - **Лицензии (#12):** моб.стек и облако — MIT-совместимо; self-hostable reference. ## 11. IPC / интерфейсы - **`ru.shturman.Companion`** (домен L; контракт — §15): локальный API для моб.приложения (через Connectivity/ лок.сеть, не публичная D-Bus); методы синка/бэкапа; `TriggerUpdateCheck()`, статус OTA; сигналы `UpdateAvailable`/`SyncStateChanged`. Применение OTA — делегируется A (RAUC). - **Потребляет:** `Settings` (зеркало/синк), память D (`.md`), **E (live-данные/DTC — read; поездки — app-storage trip-плагина, plugin-sdk §8)**, `Connectivity` (G — транспорт/`State`/metered), `Power` (B — тайминг). **Гейтинг:** `network` (security-privacy §3/§5); память/контакты — только по явному согласию (§5/§10). - **UI:** consent/паринг/уведомления-на-головблоке — слот Shell (C §4); consent-UI — владелец C (security-privacy §7). ## 12. Dev-симулятор (#13) - **Мок-облако/OTA-сервер** (раздаёт подписанные / намеренно-битые / **downgrade**-бандлы — тест верификации+ anti-rollback A и отказа L по размеру/хэшу), **фейковый companion-peer** (паринг/синк/конфликты/мульти-peer без реального телефона), **мок-телеметрия-sink** (проверка opt-in-гейта: по умолчанию ноль egress; дроп буфера на отзыв/reset). Поверх мок-сети G (включая `portal`/невалидное-время). → dev-environment. ## 13. Функции | функция | MVP/later | зависит от | фаза | |---------|-----------|------------|------| | Companion-сервис (device-side, локальный API, Stage 2) | later | G (транспорт), Settings | v3 | | Паринг device↔телефон (локальный якорь, per-peer ключ) | later | — | v3 | | Моб.приложение: данные машины/DTC (read-only) + поездки | later | E, trip-плагин, стек 🟡 | v3 | | Моб.приложение: просмотр/правка памяти водителя | later | D §7 | v3 | | Синхронизация (настройки/память/поездки, local-first) | later | Settings, D, trip-плагин | v3 | | Уведомления (local in-app / remote-relay) | later | relay (§5) | v3/v4 | | Телеметрия (opt-in, выключена; consent-wipe на reset) | later | security-privacy §7, a-base §12 | v2/v3 | | Бэкап/восстановление `/data` (+ совместимость BSP/схемы) | later | a-base §3/§11/§12 | v3/v4 | | OTA-канал: сборка/хостинг/доставка + fail-safe + anti-rollback | later | a-base §5, secure boot | v4 | | Каналы релизов (stable/beta) + staged rollout + halt-петля | later | OTA-канал | v4 | | Опциональное облако (relay/хостинг, self-hostable) | later | — | v4 | | Удалённый статус машины (read-only, opt-in локация) | later | E, consent | v4 | | Удалённый доступ через облако-relay (E2E) | later | паринг, облако | v4 | ## 14. Зависимости - **Вниз/вбок:** **A** (§5 OTA-механизм/подпись/**anti-rollback**/откат — L только канал; §3 fscrypt+backup-ключ; §7 время/TLS-gate; §9 audit/критичные события — источник телеметрии; §11/§12 restore/wipe), **G** (Connectivity: `State`/metered/portal — транспорт-гейт), **D** (§7 память — синк-цель), **E** (live-данные/DTC — read-only; **поездки — app-storage trip-плагина, plugin-sdk §8**), **J** (§4 dashcam-гейт), **B** (§4/§7 OTA/sync-тайминг), **C** (consent/паринг/уведомления-UI), security-privacy (§5/§7 гранты/телеметрия/152-ФЗ). - **Вверх:** удалённый просмотр/настройки для пользователя; OTA-доставка для всего парка. ## 15. Открытые вопросы - 🟡 **Стек моб.приложения** (Flutter vs Rust-core+native) — off-device, MIT. → реализация. - 🟡 **Push-транспорт удалённых уведомлений** (self-hosted relay vs APNs/FCM vs только-локально; 152-ФЗ/#8). → §5 + реализация. - ◻️ **Cross-device backup-ключ** (passphrase/user-held, не из eFuse; re-encrypt при экспорте) — restore зашифрованного на новой плате. → a-base §3. - ◻️ **Совместимость restore** (версия схемы Settings + BSP/vehicle-профиль; миграция vs отказ). → реализация + a-base §11/§13. - ◻️ **Модель аккаунта:** account-recovery (потеря пароля/устройства, для relay); multi-vehicle (один аккаунт — N головблоков, адресация); multi-user роли (владелец/гость, доступ к памяти водителя). → реализация/legal. - ◻️ **Право на экспорт/удаление из облака** (синк + egress-нувшая телеметрия) + retention в ingest; factory-reset не трогает облачную копию. → security-privacy §7 + legal. - ◻️ **`ru.shturman.Companion` контракт** — методы синка/OTA-trigger **+ версионный handshake моб↔головблок** (min-version, graceful-degrade при skew; on-device конвенция ipc §2/plugin-sdk §7 к off-device без манифеста неприменима). → ipc. - ◻️ **Staged rollout: halt-петля + развязка с opt-in телеметрией** (мин. health-сигнал результата OTA vs ручной gate) + механика %-парка/delta. → a-base §5 + L. - ◻️ **Разрешение конфликтов синка** памяти `.md` (merge vs LWW vs версионирование). → реализация. - ◻️ **152-ФЗ роли** (оператор/обработчик ПДн для синка/телеметрии/удалённого) — legal-трек (как BYO-LLM). - ◻️ **Download-staging бандла** (область, resume/cleanup-политика; вне durable-write контракта a-base §3, если в `/data`). → реализация. --- ## Журнал решений (домен L) | Решение | Выбор | Дата | |---------|-------|------| | Позиционирование | апсайд-слой; облако **опционально и self-hostable**; система полноценна без него (#3) | 2026-06-23 | | Архитектура | device-Companion-сервис (Stage 2) + моб.приложение + опц.облако; **local-first** device↔телефон | 2026-06-23 | | Красные линии | read-only к машине, без удалённого управления (#1/#2); память/контакты/локация/dashcam — не в облако без согласия | 2026-06-23 | | Паринг | локальный per-peer якорь (код/QR), аккаунт не обязателен; роли peer'ов / multi-vehicle / recovery — открыты | 2026-06-23 | | OTA | A — механизм/подпись/**anti-rollback**/откат; L — сборка/хостинг/доставка; untrusted-host корректен ТОЛЬКО с anti-rollback; DOWNLOAD(L)≠APPLY(A) | 2026-06-23 | | OTA fail-safe | проверка места/ENOSPC, range-resume, отбраковка битого бандла на L до RAUC; гейт `State==online`+время | 2026-06-23 | | Телеметрия | **opt-in, выключена**; не ретроспективно; отзыв/reset → дроп буфера + consent выкл; облако: retention+удаление; анонимизация; RU/self-hosted | 2026-06-23 | | Синк | local-first (настройки/память/**поездки из trip-плагина, не E**); конфликты `.md` — открыто; не в облако без согласия | 2026-06-23 | | Бэкап-шифрование | fscrypt eFuse-bound непереносим → cross-device restore требует отдельного backup-ключа (открыто, a-base §3) | 2026-06-23 | | Lifecycle | OTA не на ходу; time-gate (a-base §7); фоновые синк/телеметрия не держат wake; staging цел на shutdown | 2026-06-23 | | Моб.стек | 🟡 off-device (Flutter / Rust-core+native), MIT; Rust-first-правило прода — про головблок, не сюда | 2026-06-23 | | Фазы | телеметрия v2/v3; компаньон/синк/бэкап v3; OTA-канал/облако/удалённое v4 | 2026-06-23 |