docs(ipc): v2 — ретро-ревью (10 находок)

- enforcement: прокси даёт грубую изоляцию (сервис/метод), тонкую (per-signal,
  per-key) делает сам сервис по идентичности sender — прокси не видит payload
- PermBroker: идентичность только из соединения, CheckCapability(cap) sender-scoped
  (не принимает app аргументом → нет спуфинга); GrantChanged адресный
- НОВЫЙ сервис ru.shturman.Location (домен K) — GPS/положение; GPS-скорость не через E
- ошибки Stale/Timeout (транзиентно) vs NotAvailable (постоянно); GetSignal с таймаутом
- desired_max_hz = агрегатный hint (не per-subscriber троттл); подписки owner-bound,
  авто-снятие по NameOwnerChanged; quality-поле; BatteryVoltage→ModuleVoltage
- RegisterIntents(handler_path) — фразы из манифеста; версии-мажоры сосуществуют

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 00:49:41 +03:00
parent 231a64300d
commit 2a9f9925cc
+29 -16
View File
@@ -4,7 +4,7 @@
> Формализует карту связей из [architecture.md](../architecture.md) §5 в конкретные
> контракты. К нему цепляются data-model, plugin-sdk и все домены.
Статус: **v1 (на ревью).**
Статус: **v2 (на ревью).** v2 — после ретро-ревью (10 находок).
Связано с: [architecture.md](../architecture.md) (§45) · [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).
@@ -31,13 +31,15 @@
| Well-known имя | `ru.shturman.<Service>` (напр. `ru.shturman.VehicleData`) |
| Путь объекта | `/ru/shturman/<Service>` |
| Интерфейс | `ru.shturman.<Service>N``N` = мажорная версия (напр. `…VehicleData1`) |
| Версионирование | аддитивные изменения внутри версии; ломающиеновый интерфейс `…2` |
| Ошибки | `ru.shturman.Error.<Name>`: `PermissionDenied`, `NotAvailable`, `ReadOnly`, `InvalidArgument`, `Unsupported` |
| Версионирование | аддитивно внутри версии; ломающее**добавляется** `…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-интроспекция) фиксируются при реализации; здесь — реестр и ключевые члены.
@@ -46,10 +48,12 @@
## 3. Реестр сервисов — ядро (привилегированные)
### `ru.shturman.VehicleData` — данные машины (**READ-ONLY**)
- **Методы (только чтение):** `ListSignals() → [name]`, `GetSignal(name) → (value, unit, ts)`, `Subscribe(names, max_hz)`/`Unsubscribe(names)`, `GetDtcs() → [(code, status, desc)]`.
- **Сигналы:** `SignalChanged(name, value, ts)`**только подписчикам и только по подписанным** именам (не широковещательно «всё всем»), `DtcsChanged([...])`.
- **Политика частоты:** на шину сигналы идут **с потолком (~10–20 Гц)** — этого хватает для читаемых датчиков и держит control-plane лёгким. Сырой высокочастотный CAN остаётся внутри VehicleData. Цифровому приборному щитку, если нужна более плавная стрелка, — клиентская интерполяция или выделенный быстрый канал (не D-Bus); см. «Открытые вопросы».
- **Properties (горячие, текущее значение):** `Speed`, `Rpm`, `CoolantTemp`, `BatteryVoltage`, `Online`.
- **Методы (только чтение):** `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` — питание и жизненный цикл
@@ -60,12 +64,13 @@
### `ru.shturman.Settings` — конфигурация и состояние
- **Методы:** `Get(key) → value`, `Set(key, value)`, `List(prefix) → [key]`, `Reset(key)`.
- **Сигналы:** `Changed(key, value)`.
- Namespace на каждый ап изолирован (брокер не даёт читать чужой).
- **Namespace изолирует сам сервис Settings** по идентичности вызывающего (`sender` → app-id): Get/Set/List/Reset вне своего namespace → `PermissionDenied` (прокси этого не делает — не инспектирует строку `key`). Свой namespace — read/write по умолчанию (ambient, без отдельной capability).
### `ru.shturman.PermBroker` — выдача разрешений (привратник)
- **Методы:** `CheckCapability(app, cap) → bool`, `RequestRuntimeGrant(cap) → granted` (рантайм-промпт пользователю, напр. «ап X хочет сеть»).
- **Сигналы:** `GrantChanged(app, cap, granted)`.
- Основная фильтрация — на уровне прокси (§5); этот API для рантайм-согласий.
### `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)]`, `StartApp(id)`, `StopApp(id)`, `GetStatus(id) → status`.
@@ -75,14 +80,19 @@
### `ru.shturman.Connectivity` — сеть (обёртка NM/MM)
- **Методы:** `GetStatus() → status`, `ListNetworks() → [...]`, `Connect(id)`, `Disconnect()`.
- **Сигналы:** `ConnectivityChanged(online)`, `NetworkChanged(...)`.
- **Properties:** `Online`, `Type` (wifi/modem), `SignalStrength`.
- **Properties:** `Online` (bool — «сервис ещё не поднят» ≠ «offline»), `Type` (wifi/modem), `SignalStrength`.
### `ru.shturman.Location` — GPS/положение (владелец — домен K)
- **Properties:** `Latitude`, `Longitude`, `Heading`, `Speed` (GPS-скорость), `FixQuality`, `ts`.
- **Сигналы:** `LocationChanged(...)`, `FixChanged(quality)`.
- Источник — NMEA/gpsd (hardware §4); гейтится capability `location`. **GPS-скорость берётся ОТСЮДА** (не из VehicleData/E — E это CAN/OBD, появляется в v2). До домена K — мок-стаб (dev fake-GPS).
---
## 4. Реестр сервисов — апы (на SDK)
### `ru.shturman.Assistant`
- **Методы:** `SendText(query) → reply`, `StartListening()`, `StopListening()`, `RegisterIntents(handler_path, [pattern])` (для плагинов с capability `assistant_intents`).
- **Методы:** `SendText(query) → reply`, `StartListening()`, `StopListening()`, `RegisterIntents(handler_path)` — привязывает handler к фразам **из манифеста** `assistant_intents` (фразы НЕ передаются в рантайме, иначе обходится install-ревью).
- **Сигналы:** `StateChanged(idle|listening|thinking|speaking)`, `TranscriptUpdated(text)`, `ReplyUpdated(text)`.
- Контекст машины подмешивается ассистентом из `VehicleData` (см. architecture §5).
@@ -100,7 +110,7 @@
1. Манифест плагина декларирует capabilities (`vehicle_read:[speed]`, `network`, `assistant_intents`…).
2. App-Host поднимает плагин в bubblewrap и даёт ему **фильтрующий D-Bus-прокси**, сконфигурированный под эти capabilities.
3. Прокси пропускает только разрешённые сервисы/интерфейсы/методы/сигналы. Пример: `vehicle_read:[speed]` виден `VehicleData.GetSignal("speed")` и `SignalChanged` по speed; **не виден** `Power`, чужие апы, любые write-методы.
3. Прокси даёт **грубую** изоляцию — сервис/интерфейс/метод/сигнал (payload/аргументы НЕ инспектирует). Пример: `vehicle_read:[speed]``VehicleData` (read) **достижим**, а `Power`, чужие апы и write-методы**нет**. **Тонкую** авторизацию по конкретному имени сигнала / ключу настроек делает сам сервис по идентичности `sender` (см. VehicleData, Settings).
4. Рантайм-согласия (сеть «сейчас») — через `PermBroker.RequestRuntimeGrant`.
Итог: плагин физически не может дотянуться до того, чего нет в манифесте. Детали модели — [security-privacy.md](security-privacy.md).
@@ -140,4 +150,7 @@
|---------|-------|------|
| Топология шины | системная шина устройства + фильтрующий прокси на ап (🟡 на подтверждение) | 2026-06-16 |
| Изоляция доступа апов | портал-паттерн: per-app dbus-proxy из манифеста | 2026-06-16 |
| Соглашения имён/версий | `ru.shturman.*`, версия в имени интерфейса | 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 |