diff --git a/docs/contracts/ipc.md b/docs/contracts/ipc.md index 129b700..101bdbc 100644 --- a/docs/contracts/ipc.md +++ b/docs/contracts/ipc.md @@ -73,9 +73,16 @@ - Статическая фильтрация — прокси + sandbox (§5, владеет App-Host); broker — политика и рантайм-согласия. ### `ru.shturman.AppHost` — запуск/супервизия апов и плагинов -- **Методы:** `ListApps() → [(id, status)]`, `StartApp(id)`, `StopApp(id)`, `GetStatus(id) → status`. -- **Сигналы:** `AppStarted(id)`, `AppStopped(id)`, `AppCrashed(id, reason)`. -- (Установка/дистрибуция плагинов — домен F, не здесь.) +- **Методы:** `ListApps() → [(id, status)]`, **`LoadApp(id)`** (читает манифест `/data/apps//`, + конфигурит sandbox+прокси+`sender→манифест`; затем доступен `StartApp`), `StartApp(id)`, `StopApp(id)`, + **`UnloadApp(id)`** (снести sandbox/прокси/identity-binding), `GetStatus(id) → status`. +- **Сигналы:** `AppStarted(id)`, `AppStopped(id)`, `AppCrashed(id, reason)`, `AppLoaded(id)`. +- Установка/жизненный цикл плагинов — `ru.shturman.PluginManager` (ниже, домен F): зовёт `LoadApp`/`UnloadApp` после install/remove/update. + +### `ru.shturman.PluginManager` — установка/жизненный цикл плагинов (домен F) +- **Методы:** `InstallPlugin(pkg)`, `RemovePlugin(id)`, `UpdatePlugin(id)`, `ListPlugins() → [(id, version, status)]`. +- **Сигналы:** `PluginInstalled(id)`, `PluginRemoved(id)`, `PluginUpdated(id)` (+ эмит нормативных audit-записей, A §9). +- **Привилегированная операция** — недоступна самим плагинам через прокси. Контракт детально — домен F. ### `ru.shturman.Connectivity` — сеть (обёртка NM/MM) - **Методы:** `GetStatus() → status`, `ListNetworks() → [...]`, `Connect(id)`, `Disconnect()`. @@ -104,6 +111,12 @@ ### `ru.shturman.Media` / `…Nav` / `…Phone` - Свои интерфейсы (play/pause/route; navigateTo/cancel; dial/answer/hangup). **Детали — в спеках доменов** H/I/G; здесь — только факт регистрации на шине. +### `ru.shturman.Camera` — камеры/виды (владелец — домен J) +- **Методы:** `ListSources()`, `SelectView(id)`. +- **Сигналы:** `ReverseEngaged(on)` (источник — `Power`/GPIO, см. J §3). +- **Properties:** `SignalState` per-source (`live`/`no_signal`/`acquiring`). +- Видео — НЕ здесь (PipeWire/V4L2, data-plane); гейтится `camera_in` (security-privacy). До домена J — мок-стаб. Детали — [domains/j-cameras-video.md](../domains/j-cameras-video.md). + --- ## 5. Брокеринг доступа песочных апов (портал-паттерн) diff --git a/docs/contracts/security-privacy.md b/docs/contracts/security-privacy.md index aa54431..4e60b3c 100644 --- a/docs/contracts/security-privacy.md +++ b/docs/contracts/security-privacy.md @@ -57,6 +57,7 @@ | `audio_in` (микрофон) | запись с микрофона — **высокочувствительно** | **per-node** capture-разрешение, выдаётся в момент **runtime-гранта** (не голый сокет!); **видимый индикатор**; сторонним — ограниченно. Нужен PipeWire с Security Context (~0.3.65+) | | `network` | доступ в сеть | net-namespace + **runtime-грант**. ⚠️ Строго — только бинарно on/off; **host-allowlist** требует forced-proxy/egress-firewall по SNI/Host (DNS-allowlist обходится прямым IP), иначе best-effort (§7) | | `location` | GPS/положение | D-Bus `ru.shturman.Location` (ipc §3) + **runtime-грант** | +| `camera_in:[sources]` (камера) | захват видео с источников — **высокочувствительно** | **per-node** PipeWire Security Context + WirePlumber (video-ноды, default-deny), **runtime-грант + видимый индикатор**; `/dev/videoN` — только first-party; стороннему — узкий per-source грант | | `gpu` (render) | GPU-ускорение (UI/ML) | bubblewrap пропускает `/dev/dri` | | `storage` | приватное хранилище | bubblewrap **mount** `/data/apps//` | @@ -105,8 +106,9 @@ PipeWire-capture/location). Для `audio_in`/`location` — **while-in-use**: в v0–v3 якорь = trust-by-build / физический контроль (dev-ключи, eFuse не жжём). - **Сторонние** — в `/data`, **всегда** в песочнице; install-ревью + runtime-промпты. **Целостность id при установке:** App-Host/Perm-Broker **отвергает** плагин, чей `id` - совпадает с уже установленным или встроенным/first-party; namespace `ru.shturman.plugin.*` - зарезервирован за образом. ⚠️ Без подписи (§6) это ловит только случайные коллизии, НЕ + совпадает с уже установленным или встроенным/first-party; manifest-id namespace **`ru.shturman.*` (весь tree)** зарезервирован за first-party/образом — + сторонний id = свой reverse-DNS (`dev.example.*`). *(D-Bus OWN-экспорт плагина — отдельный + платформенный префикс `ru.shturman.plugin..*` (§4), это не manifest-id, не путать.)* ⚠️ Без подписи (§6) это ловит только случайные коллизии, НЕ намеренную подмену ещё-не-установленного легитимного id — аргумент за подпись **раньше** «маховика». ## 6. Доверие и дистрибуция (целостность манифеста) @@ -191,5 +193,6 @@ forced-proxy, §3); строгая гарантия — только бинар | Enforce-split | статически — прокси+sandbox (App-Host); рантайм-гранты — Perm-Broker; грант while-in-use, авто-снятие | 2026-06-16 | | Аудио | per-node гейтинг (PipeWire Security Context + WirePlumber), не голый сокет; capture по гранту | 2026-06-16 | | Сеть | строго — бинарно on/off; host-allowlist best-effort без forced-proxy | 2026-06-16 | -| Целостность id | install-time отказ при коллизии id; `ru.shturman.plugin.*` зарезервирован | 2026-06-16 | +| Целостность id | install-time отказ при коллизии id; manifest-id `ru.shturman.*` зарезервирован за first-party (OWN-экспорт `ru.shturman.plugin..*` — отдельный платформенный префикс) | 2026-06-16 | +| `camera_in` | per-node гейтинг видео (PipeWire Security Context, как audio_in); `/dev/videoN` — only first-party | 2026-06-16 | | Audit-log | нормативный, владелец — домен A (§9, fscrypt-поддерево); просмотр — UI домена C | 2026-06-16 | diff --git a/docs/domains/a-base-system.md b/docs/domains/a-base-system.md index d6ec62c..9866588 100644 --- a/docs/domains/a-base-system.md +++ b/docs/domains/a-base-system.md @@ -56,8 +56,8 @@ fsync(file) → rename → fsync(dir)`. Структурный RO-rootfs НЕ защищает содержимое `/data` при резком обесточивании — атомарная запись защищает даже без graceful shutdown. - **At-rest шифрование `/data` (v4, 🟡):** **fscrypt** на чувствительных поддеревьях - (память водителя `.md`, `/data/apps//`, audit-log, OAuth-токены провайдеров — - это и наполняет смыслом «защищённое хранилище» из d-assistant). Ключ привязан к + (память водителя `.md`, `/data/apps//`, audit-log, OAuth-токены провайдеров, + **dashcam-медиа (J §4)** — это и наполняет смыслом «защищённое хранилище» из d-assistant). Ключ привязан к **OTP/eFuse** (тот же анкор, что secure boot), headless-unlock (без пароля — авто стартует по ACC). Иначе изъятая eMMC/SD = открытая утечка ПДн (152-ФЗ). Связан с secure boot → оба в v4. diff --git a/docs/domains/f-plugin-host.md b/docs/domains/f-plugin-host.md new file mode 100644 index 0000000..83eddb9 --- /dev/null +++ b/docs/domains/f-plugin-host.md @@ -0,0 +1,134 @@ +# Домен F — Plugin host & экосистема + +> Рантайм и экосистема плагинов **поверх App-Host**: жизненный цикл (install/update/remove), +> валидация и подпись манифеста, дистрибуция, dev-tools. API/манифест — [plugin-sdk](../contracts/plugin-sdk.md); +> исполнение/sandbox — App-Host (ядро); **F — оркестрация и экосистема**. + +Статус: **v2 (на ревью).** v2 — после adversarial-ревью (17 находок). +Связано с: [plugin-sdk.md](../contracts/plugin-sdk.md) (API) · [security-privacy.md](../contracts/security-privacy.md) (sandbox/подпись/id) · [ipc.md](../contracts/ipc.md) (`PluginManager`, `AppHost`) · [a-base-system.md](a-base-system.md) (§3 durable-write/storage, §8 память) · домен C (capability-review UX) · все домены (точки расширения) + +--- + +## 1. Назначение и границы + +- **F:** жизненный цикл плагинов + валидация манифеста + дистрибуция + dev-tools + enforcement версий + бюджет ресурсов. +- **App-Host (ядро, `ru.shturman.AppHost`)** физически запускает/супержит/песочит. **F оркестрирует, App-Host исполняет.** +- **plugin-sdk** = API/манифест; **security-privacy** = sandbox/capabilities/доверие. F их **использует, не дублирует**. +- **Границы:** не ядро; не обходит capability-модель; не трогает CAN/safety. + +## 2. Жизненный цикл плагина + +`discover → validate → install → activate → run → update → remove`. F оркестрирует; запуск/sandbox — App-Host. +**First-party** апы путь НЕ проходят (в подписанном base-образе, security-privacy §5). + +- **Где живёт/исполняется:** пакет (manifest + бинарь + ассеты) — в `/data/apps//`. **Код — отдельное + exec-поддерево** (`/data/apps//bin`, монтируется exec в bubblewrap); данные (`storage`) — **noexec**. + Политика exec/mount на `/data` — совместно с a-base §3 (открытый вопрос §10). +- **Передача в App-Host (IPC):** после `validate`+`install` `PluginManager` зовёт **`AppHost.LoadApp(id)`** + (читает манифест, конфигурит bubblewrap + фильтр-прокси + `sender→манифест`), затем id доступен `StartApp`. + `RemovePlugin`/`UpdatePlugin` → **`AppHost.UnloadApp(id)`** (снести sandbox/прокси/identity-binding — иначе висит авторизация на шине). +- **Teardown (remove/update):** если работает → `StopApp` → await `AppStopped` (таймаут/force-kill) → + Perm-Broker отзывает гранты + подписки VehicleData падают по owner-exit → удалить `/data/apps/` + (опц. экспорт). (`NameOwnerChanged` — сеть безопасности при крэше, не плановый путь; план зовёт `StopApp` явно.) +- **Атомарность install/update** (power-safe #5, контракт a-base §3): распаковка в staging `/data/apps/.new/` + → fsync → **атомарный `rename`** на живой каталог → fsync(parent); прежняя версия цела до успеха (откат); + health-check до commit (аналог RAUC mark-good); идемпотентное доедание `.new` на next boot. + +## 2a. Бюджет ресурсов плагина + +App-Host из манифеста на activate ставит **cgroup-лимиты** (`MemoryMax`/`MemoryHigh`, `CPUQuota`, `pids.max`): +дефолтный потолок + опц. больший в манифесте (виден в ревью — как дисковая квота plugin-sdk §4.4). +В OOM-иерархии (a-base §8) сторонний плагин — **низкий приоритет** (ниже Stage-1-ядра и офлайн-LLM): +убивается раньше killer-фичи → `AppCrashed` → деградация (#4). CPU-троттлинг — чтобы busy-loop не съел UI (#11). + +## 3. Валидация манифеста (целостность — только с подписью) + +- **Schema-валидация** (plugin-sdk §2): поля; capabilities из таксономии; `len(tiles)≤ui_tiles`; `ui_screens`; + `vehicle_read` только из каталога data-model; i18n-строки (`ru` обязателен). +- **`shturman_api`:** неподдерживаемая → `Error.Unsupported` (plugin-sdk §7); **EOL установленного** (§3 ниже). +- **id-целостность** (security-privacy §5): отказ при коллизии с установленным/встроенным. **Манифестный id**: + `ru.shturman.*` (весь tree) **закрыт** для сторонних — их id = свой reverse-DNS (`dev.example.*`). *(Отдельно: + D-Bus OWN-экспорт плагина — платформа выдаёт префикс `ru.shturman.plugin..*`, это не манифестный id.)* +- **Sideload-гигиена (распаковка привилегированным до sandbox):** отбой path-traversal/symlink-escape; + лимит несжатого размера/числа записей (анти-zip-bomb); проверка места `/data`/квоты до распаковки (без частичной порчи). +- **Целостность во времени (без подписи неполна):** App-Host **ре-валидирует манифест на КАЖДОМ activate** + (capabilities из файла, не кэш с момента approve); **пин одобренного capability-набора** (хэш) — расширение + на диске после approve → форс ре-ревью, не молча. Полная целостность — только с подписью (store-фаза). +- **Аудит:** install/update/remove + grant/revoke — события **нормативного audit-log** (владелец A §9 / + security-privacy): id, версия, capability-набор, время (без payload). `PluginManager` эмитит их вместе с сигналами §7. + +## 4. Дистрибуция + +- **Sideload** (локальный пакет, на свой риск, без подписи) — v3. +- **Курируемый стор/реестр** с ревью + **обязательной подписью** — later. Пакет: каталог `manifest.yaml` + бинарь + ассеты + `locales/`. + +## 5. Capability-review и управление (UX — домен C) + +- Install-time: запрашиваемые capabilities + предупреждения о рисковом (`vehicle_read`+`network`; неподдерживаемые сигналы) — security-privacy §5/§7. +- Review/revoke грантов — UI домена C. + +## 6. Dev-tools и тест-харнесс (#13) + +- **v0 (без рантайма):** SDK-крейт `shturman-sdk`, **scaffolding** плагина, **валидатор манифеста**. +- **dev-run:** быстрый локальный запуск плагина в dev-VM (App-Host есть с v0); sandboxed install-flow — v3. + Нужен `justfile`-таргет в dev-environment. +- **Тест-харнесс plugin-host (#13):** библиотека **плохих манифестов** (коллизия id, неподдерж. `shturman_api`, + `tiles>ui_tiles`, capability вне таксономии, `vehicle_read` вне каталога, попытка `ru.shturman.*`) + + **тест-плагины** (crash-loop, mem-hog, quota-overflow, capability-escape, slow-shutdown, power-loss при update). + Завести в dev-environment (fault-injection) + интеграционный тир CI. + +## 7. IPC + +- **`ru.shturman.PluginManager`** (домен F, в реестре ipc §3): `InstallPlugin(pkg)`, `RemovePlugin(id)`, + `UpdatePlugin(id)`, `ListPlugins()`; сигналы `PluginInstalled/Removed/Updated` (+ эмит audit-записей). +- **Передача в App-Host:** `AppHost.LoadApp(id)` / `UnloadApp(id)` (ipc §3). Управление плагинами — + **привилегированная операция**, недоступна самим плагинам через прокси. + +## 8. Функции + +| функция | MVP/later | зависит от | фаза | +|---------|-----------|------------|------| +| SDK-крейт `shturman-sdk` | **MVP** | — | v0 | +| Dev-tools: scaffolding + валидатор манифеста | **MVP-dev** | — | v0 | +| Dev-run плагина в VM (через App-Host) | **MVP-dev** | App-Host, dev-environment | v0+ | +| Тест-харнесс plugin-host (#13) | **MVP-dev** | dev-environment | v0+ | +| Жизненный цикл (install/update/remove) + LoadApp/UnloadApp | later | App-Host, PluginManager, plugin-sdk | v3 | +| Атомарный install/update (staging+rename, a-base §3) | later | a-base §3 | v3 | +| Бюджет ресурсов (cgroup + OOM-приоритет) | later | a-base §8 | v3 | +| Schema/`shturman_api`/id-валидация + ре-валидация + capability-pin | later | plugin-sdk, security-privacy | v3 | +| Sideload (+ гигиена распаковки) | later | App-Host, PluginManager | v3 | +| Capability-review UX (с C) + аудит операций | later | C, security-privacy, A §9 | v3 | +| Подпись манифеста/бандла + keyring | later | security-privacy (схема §10); v4-trust-anchor (signed OTA) | v3+/v4 | +| Курируемый стор/реестр | later | — | later | + +## 9. Зависимости + +- **Вниз/вбок:** App-Host (запуск/sandbox/прокси/Load-Unload), plugin-sdk (API/манифест), security-privacy + (capabilities/доверие/подпись/namespace), a-base (§3 durable-write/exec-mount, §8 память/OOM, §9 audit-log), + data-model (валидация `vehicle_read`), C (review/revoke UI), dev-environment (dev-tools/харнесс). +- **Вверх:** механизм расширения third-party для всех доменов. + +## 10. Открытые вопросы + +- 🟡 **Exec/mount-политика `/data`** (exec-поддерево бинаря vs noexec) — совместно с a-base §3. +- 🟡 **Схема подписи** (формат, keyring издателей, ревокация, охват = манифест+бинарь) — со стором. → security-privacy + F. +- 🟡 **WASM-тир структурно несовместим с identity-моделью:** in-process WASM не имеет своего D-Bus-соединения/PID/cgroup + → sender-identity-гейтинг (VehicleData/Settings/OWN) к нему неприменим без host-side брокера в рантайме. Не просто «паковка». → architecture §7 / security-privacy. +- ◻️ **`shturman_api` EOL/депрекация** установленных: warn→grace→block-launch (не тихий крэш), роль UpdatePlugin/ревью. (тайминг — ipc §2). +- ◻️ **Governance стора** (модерация, доверие издателей). + +--- + +## Журнал решений (домен F) + +| Решение | Выбор | Дата | +|---------|-------|------| +| Граница | F — оркестрация; App-Host исполняет (LoadApp/UnloadApp); plugin-sdk — API | 2026-06-16 | +| Где живёт | `/data/apps//`; код — exec-поддерево, данные — noexec; политика с a-base §3 | 2026-06-16 | +| Жизненный цикл | install/activate/teardown с явным StopApp+отзыв грантов; first-party вне пути | 2026-06-16 | +| Атомарность | staging + атомарный rename (a-base §3 durable-write); откат; идемпотентное доедание | 2026-06-16 | +| Ресурсы | cgroup-лимиты из манифеста; плагин — низкий OOM-приоритет (a-base §8) | 2026-06-16 | +| Валидация | ре-валидация на каждом activate + capability-pin; sideload-гигиена распаковки; аудит операций | 2026-06-16 | +| Namespace | манифестный id `ru.shturman.*` закрыт для сторонних; OWN-экспорт `ru.shturman.plugin..*` — выдаёт платформа | 2026-06-16 | +| Доверие | install-approve+sandbox (sideload на риск); подпись+keyring — со стором | 2026-06-16 | +| Фазы | SDK/dev-tools/харнесс — v0; плагин-экосистема — v3 | 2026-06-16 | diff --git a/docs/domains/j-cameras-video.md b/docs/domains/j-cameras-video.md new file mode 100644 index 0000000..23e8f11 --- /dev/null +++ b/docs/domains/j-cameras-video.md @@ -0,0 +1,135 @@ +# Домен J — Камеры / видео + +> Видео-источники и виды: задняя камера, dashcam, круговой обзор. Камеры — **динамический +> набор 0..N источников** через data-plane (PipeWire/V4L2). **Не safety-critical** (#1) — +> availability-of-view, не управление. + +Статус: **v2 (на ревью).** v2 — после adversarial-ревью (20 находок). +Связано с: [hardware.md](../contracts/hardware.md) (§4) · [architecture.md](../architecture.md) (§4 data-plane, §6 Stage 0) · [a-base-system.md](a-base-system.md) (§3 fscrypt, §4 boot-инфра, §8 память, §10/§12) · [ipc.md](../contracts/ipc.md) (`Camera`) · [security-privacy.md](../contracts/security-privacy.md) (`camera_in`, приватность) · домены C, B (§7/§12), E/K + +--- + +## 1. Назначение и границы + +- **Источники видео (0..N, динамические)** + **виды** (задняя/парктроник, dashcam, surround) + оверлеи. +- **Шов с A:** boot-инфра Stage 0 раннего пути (initramfs/драйвер/DT) — **A §4**; **J** — источник/вид/оверлей. Питание/реверс-wake — **B §7**. +- **Не safety-critical** (#1): вывод картинки, не управление; никаких регуляторных claim'ов. +- **Plane:** видео — **PipeWire/V4L2** (data-plane); control/состояние — лёгкий D-Bus (`Camera`). + +## 2. Камеры как 0..N источников + +- **V4L2/CVBS capture → PipeWire video** (аппаратный VPU — кодирование, §9); набор **динамический**; + плагин может зарегистрировать источник/вид. Низколатентный путь задней — почти прямой V4L2/DMABUF. + +## 3. Задняя камера и ранний путь (Stage 0) + +- **Stage 0 рисуется ДО Shell/smithay** — минимальным **DRM/KMS-клиентом** из initramfs (V4L2/DMABUF→KMS-плоскость, + **без Wayland/слот-протокола**, которых ещё нет). Wayland-поверхностный вид (§6) приходит позже, **поверх** этого кадра. + - **DRM-master:** ранний клиент держит DRM-master в Stage 0; на Stage 1 — передача smithay-композитору **без чёрного + кадра** (Shell поднимает overlay-слот с live-видом, затем ранний клиент отпускает master). Boot-инфра — A §4. + - **Риск (открытый):** ранний кадр до проверки подписи initramfs подрывает trust-chain, после — бьёт TTFF — + развести minimal-trust (до) vs полный вид (после), с a-base §4. +- **Сигнал реверса:** **GPIO фонаря з.х. — рекомендуемый дефолт** (надёжнее/быстрее/Stage-0-совместим). Владелец — + **`Power` (B §7)**, J **потребляет**; CAN-gear как альтернатива требует **нового gear-сигнала в data-model/E** + (его сейчас нет — ветка нереализуема без этого; gear может быть не на OBD-порту, E §7). 🟡 выбрать (§10). +- **Парктроник-оверлей:** дистанция из E (CAN) или K (не-CAN); на `stale`/`unavailable` (E §5b) — **скрыть/пометить** + оверлей, не рисовать last-known. +- **Fail-safe «нет сигнала»:** per-frame timestamp + **frame-watchdog** (нет кадра N мс / потеря sync → `no_signal`); + **запрещено** показывать последний DMABUF-буфер на таймауте — явный no-signal-оверлей (#4). Детектор — J-источник; состояние — `Camera.SignalState`. +- **Латентность — инженерный бюджет** (#11), не safety (#1). Фаза: **v2**. + +## 4. Dashcam (видеорегистратор) + +- **Непрерывная кольцевая (ring-buffer) запись — дефолт, без триггера** (K §4: не на гарантированный g-sensor). + Event-маркировка опц.: IMU **или** резкое замедление по GPS-`Speed` (K). +- **Хранилище:** loop на **отдельном носителе/разделе** (износ — A §10), ограничение размера, retention. + **ENOSPC/носитель отсутствует** → стоп записи с видимой индикацией, не крэш и не слив в `/data`. +- **Приватность (152-ФЗ, высокочувствительно):** записи содержат лица/номера/салон + локацию/время. + **On-device по умолчанию**; экспорт — только по capability + грант, доступ в audit-log; **at-rest fscrypt** + (a-base §3 — добавить dashcam-поддерево); retention + **factory-reset wipe** (a-base §12); **видимый индикатор записи**. + В онлайн-LLM/облако — никогда без явного согласия (security-privacy §7). +- Фаза: задел (later). + +## 5. Круговой обзор (surround / 360°) + +- Композит из нескольких источников (later задел); калибровка геометрии — открытый вопрос. +- **Ресурсы:** N одновременных capture + композит + warp — бюджет память/GPU/decode/шина (a-base §8, hardware §1a/§4); + приоритет vs низколатентная задняя (кто уступает DMABUF/decode — арбитраж с C §4). Surround уступает UI (#11). + +## 6. Виды в shell + +- Камера-виды (steady-state, после Stage 1) — **Wayland-поверхности** в слотах shell (C §4, slot-протокол). + Задняя/парктроник — приоритетный/оверлейный слот (может поверх раннего Stage-0-кадра). + +## 7. Камера и lifecycle (шов с B/E/A) + +- **Dashcam в sleep/battery-cutoff:** по умолчанию **НЕ пишет** на заглушённом (B §7 гейт, экономия crank-энергии); + «пишет всегда» (§4) = в пределах `running`/`accessory`, не sleep. Parking-dashcam — опц. later под арбитражем B (бюджет — B §12). +- **Реверс-wake + grace-hold рейла:** J — **запросчик** удержания дисплей/камера-рейла (через `Power`, B §7/§4-шаг2/§12); + **B — владелец/арбитр**, ограничивает hold-up-бюджетом, не переопределяет PONR. (Закрыть совместно с B §12.) +- **На `ShutdownImminent`** (B §4): durable-флаш текущего видеофрагмента ДО PONR; **сегментная нарезка** + atomic-write + (a-base §3) → резкий обрыв теряет максимум последний незакрытый сегмент. + +## 8. IPC / data-plane + +- **Видео — PipeWire/V4L2** (data-plane). +- **`ru.shturman.Camera`** (домен J, регистрируется в ipc §4): `ListSources()`, `SelectView(id)`, + `SignalState` (per-source: `live`/`no_signal`/`acquiring`); сигнал `ReverseEngaged` (источник — `Power`, §3). +- **Гейтинг:** доступ плагинов — capability **`camera_in:[sources]`** (security-privacy §3, per-node PipeWire + + WirePlumber, как `audio_in`); прямой `/dev/videoN` — **только first-party** J-сервис, плагины — только PipeWire-нода. + +## 9. Ресурсы видео + +- **Кодирование dashcam — на аппаратном VPU RK3588** (rkmpp/V4L2-M2M), не CPU (H.264/H.265). 🟡 кодек/VPU. +- **DMABUF zero-copy** capture→encode/composite (N источников × буферы — главный член RAM). +- Внести видео-пайплайн в **a-base §8** (давление памяти + OOM-порядок: задняя — защищена у Stage-1; dashcam/surround — throttleable). +- GPU-контеншн: surround уступает UI (#11); деградация при throttling (a-base §10) — видимая, не stall. + +## 10. Функции + +| функция | MVP/later | зависит от | фаза | +|---------|-----------|------------|------| +| Задняя камера (capture + вид) + Stage 0 ранний путь (DRM/KMS) | later | hardware, A §4, B §7, C | v2 | +| Парктроник-оверлей (с учётом stale) | later | E/K | v2 | +| Fail-safe «нет сигнала» (frame-watchdog → SignalState) | later | — | v2 | +| Камера-вид как Wayland-поверхность | later | C slot-протокол | v2 | +| `camera_in` capability + per-node гейтинг | later | security-privacy | v2 | +| Dev-симулятор камер (#13) | **MVP-dev** | dev-environment (PipeWire/vcan) | v0+ | +| Камеры 0..N (динамический набор) | later | data-plane | later | +| Dashcam (ring-buffer + privacy/fscrypt + ShutdownImminent-флаш) | later | A §3/§10/§12, B §4 | later | +| Surround / 360° (+ бюджет ресурсов) | later | a-base §8, hardware | later | + +## 11. Dev-симулятор камер (#13) + +Fake V4L2/PipeWire-источник: тест-паттерн / реплей файла / **no-signal** (для fail-safe §3); эмуляция реверса +**обоих путей** (GPIO-стаб И CAN-gear через vcan — выбор не сделан); Stage-0-мок (замер TTFF); отказ capture-чипа/потеря источника. +Поверх существующих PipeWire/vcan/weston (dev-environment). Сценарий «реверс→камера» (e2e) — отсюда. + +## 12. Зависимости + +- **Вниз:** hardware (§4 capture/реверс/камеры; §1a тепло), A (§4 Stage-0 boot-инфра, §3 fscrypt/atomic, §8 память, §10/§12). +- **Вбок:** B (§7 питание/реверс-wake, §4 ShutdownImminent/grace-hold, §12), E/K (парктроник, stale), K (IMU/GPS dashcam), C (слоты/slot-протокол). +- **Вверх:** виды камер для UI (C); расширяемо плагинами (`camera_in`). + +## 13. Открытые вопросы + +- 🟡 **Сигнал реверса** GPIO (рек.) vs CAN gear — выбрать; CAN требует gear-сигнал в data-model/E. +- 🟡 **DRM-master handoff** Stage 0→Stage 1 (без чёрного кадра) + trust до/после проверки initramfs — с A §4/B. +- 🟡 **Кодек/VPU** dashcam (H.264/H.265, rkmpp) — выбрать. +- ◻️ **Носитель dashcam** (раздел/карта; износ; retention) — с A/hardware. +- ◻️ **Калибровка surround** (геометрия) — later. +- ◻️ **Капасити-бюджет** (per-source RAM, VPU-нагрузка, GPU-доля) — числа позже. + +--- + +## Журнал решений (домен J) + +| Решение | Выбор | Дата | +|---------|-------|------| +| Модель | 0..N источников через PipeWire/V4L2; виды — Wayland-поверхности в слотах C | 2026-06-16 | +| Stage 0 | ранний кадр — DRM/KMS из initramfs (не Wayland); DRM-master handoff smithay на Stage 1 без чёрного кадра | 2026-06-16 | +| Реверс | GPIO фонаря з.х. (дефолт), владелец Power/B; CAN-gear требует gear-сигнал в E (нет) | 2026-06-16 | +| Fail-safe | frame-watchdog → `no_signal`; не показывать stale DMABUF (#4); `Camera.SignalState` | 2026-06-16 | +| Гейтинг | capability `camera_in` (per-node PipeWire, как audio_in); `/dev/videoN` — только first-party | 2026-06-16 | +| Dashcam | ring-buffer; **privacy: fscrypt/retention/factory-reset/индикатор (152-ФЗ)**; VPU-кодирование; ShutdownImminent-флаш сегментами | 2026-06-16 | +| Lifecycle | не пишет в sleep; реверс-wake/grace-hold — запрос к B (арбитр B) | 2026-06-16 |