b9747ee66d
F (plugin-host, 17): - exec-путь плагина (/data/apps/<id>/, код exec-поддерево / данные noexec); IPC-передача Install→App-Host (LoadApp/UnloadApp); teardown с явным StopApp+отзыв грантов; атомарный install/update (staging+rename, a-base §3); ре-валидация манифеста на каждом activate + capability-pin; sideload-гигиена распаковки; бюджет ресурсов (cgroup+OOM); аудит операций; namespace manifest-id ru.shturman.* vs OWN-экспорт ru.shturman.plugin.<id>.*; WASM↔identity-конфликт; PluginManager в ipc; dev-tools/тест-харнесс (#13); фазы (SDK/dev v0, экосистема v3) J (cameras, 20): - Stage 0 ранний путь = DRM/KMS из initramfs (не Wayland) + DRM-master handoff smithay без чёрного кадра; реверс-сигнал owner=Power/GPIO (CAN-gear требует сигнал в E); camera_in capability + per-node видео-гейтинг (/dev/videoN only first-party); fail-safe frame-watchdog → no_signal (не stale DMABUF); dashcam privacy (152-ФЗ, fscrypt, retention, factory-reset, индикатор); lifecycle (не пишет в sleep, ShutdownImminent-флаш сегментами, grace-hold via B); surround/video ресурсы (VPU/DMABUF/a-base §8); dev-симулятор камер (#13); Camera в ipc Кросс-док: ipc (AppHost.LoadApp/UnloadApp, PluginManager, Camera), security-privacy (namespace-фикс ru.shturman.*, camera_in, audit-scope), a-base §3 (dashcam в at-rest fscrypt) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
170 lines
18 KiB
Markdown
170 lines
18 KiB
Markdown
# Контракт: IPC (D-Bus)
|
||
|
||
> Backbone связей: реестр D-Bus-сервисов, их интерфейсов и соглашений.
|
||
> Формализует карту связей из [architecture.md](../architecture.md) §5 в конкретные
|
||
> контракты. К нему цепляются data-model, plugin-sdk и все домены.
|
||
|
||
Статус: **v2 (на ревью).** v2 — после ретро-ревью (10 находок).
|
||
Связано с: [architecture.md](../architecture.md) (§4–5) · [data-model.md](data-model.md) · [plugin-sdk.md](plugin-sdk.md) · [security-privacy.md](security-privacy.md)
|
||
|
||
Область: **control plane** (D-Bus). Аудио/видео/графика — НЕ здесь (PipeWire/Wayland, см. architecture §4).
|
||
|
||
---
|
||
|
||
## 1. Шина и топология
|
||
|
||
- **Одна системная шина устройства** (Штурман — single-purpose appliance, системная шина *и есть* его шина) со строгой D-Bus-политикой.
|
||
- Ядро-сервисы общаются на ней напрямую.
|
||
- **Песочные апы/плагины напрямую шину НЕ видят** — каждому выдаётся
|
||
**фильтрующий D-Bus-прокси** (паттерн `xdg-dbus-proxy`), настроенный App-Host'ом
|
||
из capabilities манифеста (см. §5). Это реализация портал-модели из решения №5.
|
||
|
||
> 🟡 *Мелкий выбор на подтверждение:* системная шина устройства (рекомендую,
|
||
> проще) vs выделенный экземпляр `dbus-daemon` только для Штурмана (чище изоляция
|
||
> от хост-сервисов, но лишний компонент). Прокси-фильтрация даёт изоляцию в обоих
|
||
> случаях.
|
||
|
||
## 2. Соглашения
|
||
|
||
| Аспект | Правило |
|
||
|--------|---------|
|
||
| Well-known имя | `ru.shturman.<Service>` (напр. `ru.shturman.VehicleData`) |
|
||
| Путь объекта | `/ru/shturman/<Service>` |
|
||
| Интерфейс | `ru.shturman.<Service>N` — `N` = мажорная версия (напр. `…VehicleData1`) |
|
||
| Версионирование | аддитивно внутри версии; ломающее → **добавляется** `…2` рядом со старым `…1`; оба сосуществуют в окне поддержки (старые плагины работают без правок; EOL мажора синхронизируется с `shturman_api` в манифесте) |
|
||
| Ошибки | `ru.shturman.Error.<Name>`: `PermissionDenied`, `NotAvailable` (PID не поддержан машиной — постоянно), `Stale`/`Timeout` (транзиентно: таймаут/устарело), `ReadOnly`, `InvalidArgument`, `Unsupported` |
|
||
| Действие | **метод** |
|
||
| Изменение состояния | **сигнал** |
|
||
| Текущее состояние | **property** (+ стандартный `PropertiesChanged`) |
|
||
| Асинхронность | всё async (zbus + tokio); долгие операции не блокируют |
|
||
| Стандартные интерфейсы | `org.freedesktop.DBus.{Properties,Introspectable,Peer}` |
|
||
| Идентичность вызывающего | по **аутентифицированному соединению** (`sender` → PID/cgroup → манифест, привязка App-Host'ом), НЕ по аргументу метода; подменить нельзя |
|
||
| Жизненный цикл клиент-состояния | серверное состояние клиента (подписки, регистрации тайлов/интентов) **снимается автоматически** при исчезновении владельца имени (`NameOwnerChanged`) — крэш/рестарт не оставляет мусора |
|
||
|
||
> Полные сигнатуры (XML-интроспекция) фиксируются при реализации; здесь — реестр и ключевые члены.
|
||
|
||
---
|
||
|
||
## 3. Реестр сервисов — ядро (привилегированные)
|
||
|
||
### `ru.shturman.VehicleData` — данные машины (**READ-ONLY**)
|
||
- **Методы (только чтение):** `ListSignals() → [(name, availability)]`, `GetSignal(name) → (value, unit, ts, quality)`, `Subscribe(names, desired_max_hz)`/`Unsubscribe(names)`, `GetDtcs() → [(code, status, desc)]`.
|
||
- **Авторизация по имени сигнала — в самом сервисе:** прокси (§5) не видит payload, поэтому `vehicle_read:[…]` enforce-ит **VehicleData по идентичности вызывающего** (`sender` → манифест), отвергая незаявленные имена `PermissionDenied`. Сигналы **адресные**: `SignalChanged(name, value, ts, quality)` шлётся только подписчику и только по `Subscribe ∩ манифест`; `DtcsChanged([...])`.
|
||
- **Частота:** `desired_max_hz` — верхняя граница, которую готов принять подписчик, и вход в **агрегатный** опрос: фактический опрос PID = `max()` запрошенных, ограничен потолком шины (~10–20 Гц) и источником. Доставка индивидуально не троттлится — слабый клиент (приборка) прореживает сам. Имя `desired_` подчёркивает: не гарантия «моей частоты».
|
||
- **Подписки привязаны к владельцу соединения** — снимаются автоматически при крэше/рестарте; агрегатный опрос пересчитывается, опрос PID останавливается без живых подписчиков (бережёт скудный ELM327).
|
||
- **Таймаут/staleness:** `GetSignal` имеет таймаут → `Stale`/`Timeout` (или stale-flag в `quality`), не виснет на медленном ELM327-PID. `NotAvailable` = PID не поддержан машиной (постоянно); `Stale` = транзиентно (двигатель заглушен/таймаут).
|
||
- **Properties (горячие):** `Speed`, `Rpm`, `CoolantTemp`, `ModuleVoltage` (питание ЭБУ ≈ бортсеть, не клеммы АКБ), `Online`.
|
||
- ⛔ **Методов записи нет** — ни `SetSignal`, ни actuator-команд, ни clear-DTC. Архитектурная гарантия read-only (принцип #2). Типы — [data-model.md](data-model.md).
|
||
|
||
### `ru.shturman.Power` — питание и жизненный цикл (домен B)
|
||
- **Методы:** `GetPowerState() → state` (enum `off`/`accessory`/`running`/`shutting_down`/`sleep`/`battery_cutoff`), `RequestSleep()` (внутр.).
|
||
- **Сигналы:** `AccChanged(on)`, `ShutdownImminent(seconds, reason)` (`reason ∈ acc_off|under_voltage|thermal|battery_cutoff`), **`ShutdownAborted()`** (re-power до PONR), `Sleep()`, `Wake()`.
|
||
- **Properties:** `IgnitionState` (off/accessory/running — **канон**; E зеркалит, не дублирует), `Uptime` (монотонные часы), `PowerSource` (`vehicle_12v`/`holdup_cap`/`sleep_rail`/`low_battery`).
|
||
|
||
### `ru.shturman.Settings` — конфигурация и состояние
|
||
- **Методы:** `Get(key) → value`, `Set(key, value)`, `List(prefix) → [key]`, `Reset(key)`.
|
||
- **Сигналы:** `Changed(key, value)`.
|
||
- **Namespace изолирует сам сервис Settings** по идентичности вызывающего (`sender` → app-id): Get/Set/List/Reset вне своего namespace → `PermissionDenied` (прокси этого не делает — не инспектирует строку `key`). Свой namespace — read/write по умолчанию (ambient, без отдельной capability).
|
||
|
||
### `ru.shturman.PermBroker` — политика разрешений (привратник)
|
||
- **Идентичность — только из соединения** (`sender` → манифест), **никогда из аргумента** (иначе спуфинг/проверка за чужой ап).
|
||
- **Методы:** `CheckCapability(cap) → bool` (sender-scoped — ап спрашивает про СЕБЯ; кросс-ап форма доступна только привилегированным core по D-Bus-политике), `RequestRuntimeGrant(cap) → granted` (рантайм-промпт; целевой ап = `sender`).
|
||
- **Сигналы:** `GrantChanged(cap, granted)` — адресно владельцу гранта (чужие не получают).
|
||
- Статическая фильтрация — прокси + sandbox (§5, владеет App-Host); broker — политика и рантайм-согласия.
|
||
|
||
### `ru.shturman.AppHost` — запуск/супервизия апов и плагинов
|
||
- **Методы:** `ListApps() → [(id, status)]`, **`LoadApp(id)`** (читает манифест `/data/apps/<id>/`,
|
||
конфигурит 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()`.
|
||
- **Сигналы:** `ConnectivityChanged(online)`, `NetworkChanged(...)`.
|
||
- **Properties:** `Online` (bool — «сервис ещё не поднят» ≠ «offline»), `Type` (wifi/modem), `SignalStrength`.
|
||
|
||
### `ru.shturman.Location` — GPS/положение (владелец — домен K)
|
||
- **Properties:** `Latitude`, `Longitude`, `Heading`, `Speed`, `FixQuality` (enum `no_fix`/`fix_2d`/`fix_3d`/`augmented`/`dead_reckoning`), `HDOP`, `satellites`, `ts`; per-property quality для `Speed`/`Heading` (зануление у нуля — domain K §2).
|
||
- **Сигналы:** `LocationChanged(...)`, `FixChanged(quality)`.
|
||
- Источник — NMEA/gpsd (hardware §4); гейтится capability `location`. **GPS-скорость — ОТСЮДА** (не из VehicleData/E). До домена K — мок-стаб (dev fake-GPS).
|
||
|
||
---
|
||
|
||
## 4. Реестр сервисов — апы (на SDK)
|
||
|
||
### `ru.shturman.Assistant`
|
||
- **Методы:** `SendText(query) → reply`, `StartListening()`, `StopListening()`, `RegisterIntents(handler_path)` — привязывает handler к фразам **из манифеста** `assistant_intents` (фразы НЕ передаются в рантайме, иначе обходится install-ревью).
|
||
- **Сигналы:** `StateChanged(idle|listening|thinking|speaking)`, `TranscriptUpdated(text)`, `ReplyUpdated(text)`.
|
||
- Контекст машины подмешивается ассистентом из `VehicleData` (см. architecture §5).
|
||
|
||
### `ru.shturman.Shell` — UX-host (слот-модель, решение №6)
|
||
- **Методы:** `RegisterTile(decl)`, `RegisterScreen(decl) → slot_token` (для поверхности клиент предъявляет slot-token в Wayland slot-протоколе `ru.shturman.shell_slot`, не передаёт `wl_surface` по D-Bus — см. c-shell §4), `RequestForeground(slot)`, `ShowOverlay(decl)`.
|
||
- **Сигналы:** `ForegroundChanged(app)`, `NavigatedTo(screen)`, `DistractionModeChanged(level)`.
|
||
- Декларативный вклад = данные (рендерит shell); богатый = Wayland-поверхность.
|
||
|
||
### `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. Брокеринг доступа песочных апов (портал-паттерн)
|
||
|
||
1. Манифест плагина декларирует capabilities (`vehicle_read:[speed]`, `network`, `assistant_intents`…).
|
||
2. App-Host поднимает плагин в bubblewrap и даёт ему **фильтрующий D-Bus-прокси**, сконфигурированный под эти capabilities.
|
||
3. Прокси даёт **грубую** изоляцию — сервис/интерфейс/метод/сигнал (payload/аргументы НЕ инспектирует). Пример: `vehicle_read:[speed]` → `VehicleData` (read) **достижим**, а `Power`, чужие апы и write-методы — **нет**. **Тонкую** авторизацию по конкретному имени сигнала / ключу настроек делает сам сервис по идентичности `sender` (см. VehicleData, Settings).
|
||
4. Рантайм-согласия (сеть «сейчас») — через `PermBroker.RequestRuntimeGrant`.
|
||
|
||
Итог: плагин физически не может дотянуться до того, чего нет в манифесте. Детали модели — [security-privacy.md](security-privacy.md).
|
||
|
||
---
|
||
|
||
## 6. Точки расширения через IPC
|
||
|
||
- **Интенты ассистента:** плагин реализует `ru.shturman.IntentHandler1.HandleIntent(intent_id, slots) → result` и регистрируется через `Assistant.RegisterIntents`. Латентно-критичные интенты (громче, домой) ассистент обрабатывает локально без LLM.
|
||
- **Вклад в UI:** плагин зовёт `Shell.RegisterTile/RegisterScreen` (декларативно или поверхностью).
|
||
|
||
Полные контракты SDK (манифест, биндинги, точки расширения) — [plugin-sdk.md](plugin-sdk.md).
|
||
|
||
---
|
||
|
||
## Открытые вопросы (найдено на self-review → роутинг в нужные доки)
|
||
|
||
Не теряем; решается в указанных документах.
|
||
|
||
- ✅ **Гейтинг data-plane, не только D-Bus.** → решено в [security-privacy.md](security-privacy.md) §4
|
||
(PipeWire/Wayland гейтятся capability'ями через bubblewrap-сокеты). API-часть — в plugin-sdk.
|
||
- ✅ **Экспорт объектов из песочницы.** → решено в [security-privacy.md](security-privacy.md) §4
|
||
(плагин OWN под префиксом `ru.shturman.plugin.<id>.*`).
|
||
- ✅ **Приватное хранилище апа.** → решено в [security-privacy.md](security-privacy.md) §4
|
||
(`storage` → mount `/data/apps/<id>/`).
|
||
- ✅ **Коллизия интентов.** → решено в [plugin-sdk.md](plugin-sdk.md) §5 (слоистая
|
||
политика разрешения); тонкости LLM-роутинга — домен D.
|
||
- 🟡 **Целостность манифеста.** Модель доверия — [security-privacy.md](security-privacy.md) §6;
|
||
подпись/стор — задел домена F.
|
||
- ◻️ **Быстрый канал для приборки.** → [architecture.md](../architecture.md) §4 + домен E (**открыто**).
|
||
|
||
---
|
||
|
||
## Журнал решений (ipc)
|
||
|
||
| Решение | Выбор | Дата |
|
||
|---------|-------|------|
|
||
| Топология шины | системная шина устройства + фильтрующий прокси на ап (🟡 на подтверждение) | 2026-06-16 |
|
||
| Изоляция доступа апов | портал-паттерн: per-app dbus-proxy из манифеста | 2026-06-16 |
|
||
| Соглашения имён/версий | `ru.shturman.*`, версия в имени интерфейса; мажоры сосуществуют в окне поддержки (аддитивно) | 2026-06-16 |
|
||
| Enforcement | грубо — прокси (сервис/метод); тонко (per-signal/per-key) — сам сервис по `sender` | 2026-06-16 |
|
||
| Идентичность | только из аутентиф. соединения, не из аргумента; клиент-состояние снимается по `NameOwnerChanged` | 2026-06-16 |
|
||
| Location/GPS | отдельный сервис `ru.shturman.Location` (домен K); GPS-скорость не через E | 2026-06-16 |
|