Files
shturman/docs/domains/l-cloud-companion.md
kk0t9 1eaa0b8896 docs(domain L): v2 — облако/компаньон (Companion+OTA-канал+телеметрия), после adversarial-ревью (21 находка) + кросс-док
Новый домен L: device-Companion-сервис + моб.приложение + опц.self-hostable облако.
Облако опционально, local-first, никогда не управляет машиной. Многоагентный
adversarial-ревью: 30 находок, 21 подтверждена (default-refute), все применены.

Ключевое из ревью:
- OTA untrusted-host неполон без anti-rollback → добавлена монотонность security-version (downgrade/replay-защита); подпись ≠ свежесть. Зеркально в a-base §5.
- fscrypt-ключ eFuse-bound непереносим → честно: cross-device restore требует отдельного backup-ключа (открыто); убрана ложная «ключ у пользователя».
- DOWNLOAD(L) ≠ APPLY(A): разведены фазы; download-fail-safe (ENOSPC/resume/отбраковка битого до RAUC) симметрично J/H.
- Time-gate (a-base §7): холодный boot 1970 → TLS «not yet valid» — OTA/sync/телеметрия ждут вменяемых часов.
- Captive-portal (G): egress только при State==online, не на portal/limited.
- Телеметрия: consent/буфер стираются на factory-reset (инвариант «после reset все opt-in выкл»); не ретроспективна; отзыв → дроп буфера; облако: retention+удаление (152-ФЗ).
- Поездки — из trip-плагина (app-storage), не из core E.
- dashcam-бэкап: отдельный носитель + consent-гейт J §4.
- Staged-rollout halt-петля + парадокс с opt-in телеметрией; push-доставка; модель аккаунта (recovery/multi-vehicle/multi-user); version-skew моб↔головблок — зафиксированы.

Кросс-док: a-base §5 (anti-rollback + Журнал), §12 (telemetry-consent в wipe);
architecture §3 (Companion first-party) + §5 (ребро Companion→Connectivity).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 13:55:40 +03:00

28 KiB
Raw Permalink Blame History

Домен L — Облако / компаньон

Апсайд-слой поверх локальной системы: мобильное приложение-компаньон, синхронизация/бэкап, OTA-канал (сборка/хостинг/доставка) и opt-in телеметрия. Облако опционально и self-hostable — система полноценна без него (vision: открыта в любом случае, #3). Никогда не управляет машиной.

Статус: v2 (на ревью). v2 — после adversarial-ревью (21 находка). Связано с: a-base-system.md (§5 OTA-механизм/подпись/anti-rollback, §3 fscrypt, §7 время/TLS-gate, §9 audit, §12 factory-reset) · security-privacy.md (§5 грант, §7 телеметрия/152-ФЗ) · ipc.md (Settings, Power, Connectivity) · 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 (#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 (v0v3): 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