Новый домен 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>
28 KiB
Домен 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 (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 |