Files
shturman/docs/contracts/plugin-sdk.md
T
kk0t9 64ef300ef4 docs(plugin-sdk): v2 — ретро-ревью (6 находок)
- версионирование: shturman_api ↔ набор D-Bus-мажоров; хост экспортирует все
  поддерживаемые мажоры параллельно; неподдерж. версия → Error.Unsupported (не тихо)
- IntentHandler: обязательный dispatch-таймаут; крэш/таймаут/NotAvailable →
  деградированный ответ (#4), пайплайн не виснет (#11)
- vehicle_read: per-vehicle Unavailable (Subscribe ок, SignalChanged молчит, плагин
  деградирует); install-ревью предупреждает о неподдерживаемых сигналах
- авторитет манифеста: extension_points.tiles авторитетны, ui_tiles — квота (len ≤ N)
- storage: квота (ENOSPC плагину не системе — бережёт /data), remove чистит, миграция
- lifecycle: discover/install только для сторонних; first-party — в base-образе, без install
- network host-allowlist приведён к best-effort (синхрон с security-privacy)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:02:43 +03:00

13 KiB
Raw Blame History

Контракт: Plugin / Extension SDK

Главный API платформы. Как расширять Штурман, не трогая ядро. Синтез ipc.md + data-model.md + security-privacy.md. Приоритет №1: даже наши first-party апы строятся на этом SDK.

Статус: v2 (на ревью). v2 — после ретро-ревью (6 находок). Связано с: architecture.md (#1 тонкое ядро) · ipc.md · data-model.md · security-privacy.md · домены C, F


1. Принцип

  • Плагин = отдельный процесс, говорит по D-Bus через фильтрующий прокси, в bubblewrap-песочнице. Видит только заявленное в манифесте.
  • Тот же SDK — у first-party апов (с расширенными capabilities). Если для фичи «нужно лезть в ядро» — это баг API, чиним API (принцип #9).

2. Манифест (полная схема)

Формат — YAML. Единая модель: всё, что нужно плагину, — это capabilities (объединили capabilities+permissions из исходной спеки в один список).

plugin:
  id: ru.shturman.plugin.fuel-tracker   # reverse-DNS, уникальный
  name: "Учёт расхода"
  version: 0.1.0                          # semver
  description: "Пробег и средний расход"
  author: "Имя / организация"
  shturman_api: "1"                       # мажорная версия API (совместимость)

  capabilities:
    vehicle_read: [speed, maf, fuel_level, fuel_rate]   # только из каталога data-model
    assistant_intents:
      - "сколько я проехал"
      - "средний расход"
    ui_tiles: 1
    storage: true
    # network: { hosts: ["api.example.ru"] }   # опц.: host-allowlist (см. §3)

  extension_points:
    tiles:
      - id: consumption
        title: "Расход"
    intents:
      handler: /ru/shturman/plugin/fuel_tracker/intents   # путь IntentHandler

Источник истины и согласованность: extension_points.tiles — авторитетный список конкретных тайлов; capabilities.ui_tiles: Nквота (Perm-Broker enforce-ит len(tiles) ≤ ui_tiles, иначе install отклоняется InvalidArgument). Все фразы assistant_intents маршрутизируются в единственный extension_points.intents.handler.

3. Capability-модель

  • Таксономия и гейтинг по каналам — в security-privacy.md §3–4 (не дублируем). Манифест декларирует → enforce (статически — прокси+sandbox от App-Host; рантайм-гранты — Perm-Broker; security-privacy §5).
  • vehicle_read принимает только сигналы из каталога data-model.md.
  • network — host-allowlist: network: { hosts: [...] } — намерение ограничить egress. ⚠️ Строгая гарантия требует forced-proxy/egress-firewall по SNI/Host (DNS-allowlist обходится прямым IP); иначе allowlist — best-effort, строго работает только бинарный network on/off (security-privacy §3/§7). Голый network: true + vehicle_read/locationповышенный риск при установке.

4. Точки расширения

4.1 UI — тайлы и экраны (слот-модель, решение №6)

  • Shell.RegisterTile(decl)декларативный элемент (рендерит shell в едином стиле).
  • Shell.RegisterScreen(decl | surface) — декларативный экран или Wayland-поверхность (богатый ап рисует сам).
  • Декларативный словарь (констрейнт): icon, title, value, state, привязки к данным, action. Сложнее этого → поверхность. (🟡 глубину декларативного DSL уточним при домене C/Shell.)

4.2 Интенты ассистента

  • Манифест: assistant_intents: [фразы]. Плагин реализует ru.shturman.IntentHandler1.HandleIntent(intent_id, slots) → result и регистрируется через Assistant.RegisterIntents (ipc §6).
  • Латентно-критичные встроенные интенты (громче, домой) ассистент держит сам, без плагинов.
  • Контракт отказа/таймаута: HandleIntent ограничен обязательным dispatch-таймаутом (из бюджета латентности d-assistant §10). На таймаут / крэш плагина (AppHost.AppCrashed) / NotAvailable — ассистент даёт деградированный ответ («не получилось спросить X», #4), голосовой пайплайн не виснет (#11). Долгие интенты — опц. streaming/progress, не блокирующий вызов.

4.3 Данные машины (read-only)

  • vehicle_readVehicleData.Subscribe(names, desired_max_hz) / GetSignal (ipc §3). Только чтение, только заявленные сигналы.
  • Per-vehicle Unavailable: заявленный сигнал может быть не поддержан конкретной машиной (availability/NotAvailable, data-model §3) — Subscribe успешен, но SignalChanged не идёт, пока сигнал недоступен; плагин деградирует (#4), не падает. На install-ревью (§6) заявленные vehicle_read, которых нет у текущего авто, показываются предупреждением (best-effort).

4.4 Настройки и хранилище

  • Свой namespace в Settings + приватное хранилище (storage/data/apps/<id>/).
  • Квота: у /data/apps/<id>/ дефолтный размерный лимит (+ опц. больший в манифесте), enforce на записи (ENOSPC плагину, не системе) — /data журналируемый, от его места зависит graceful-flush (a-base §3/§9); неограниченный плагин не должен его исчерпать. Механизм (project-quota / учёт App-Host) — домен A/F.
  • Remove: при удалении плагина /data/apps/<id>/ удаляется (опц. экспорт до удаления) — не оставляем сирот (отдельно от factory-reset, a-base §12).
  • Миграция: плагин может объявить версию формата хранилища; на мажорный update мигрирует данные сам (платформа лишь сигналит «есть старые данные»).

5. Разрешение коллизии интентов (резолв открытого вопроса)

Когда два плагина регистрируют пересекающиеся фразы — слоистая политика:

  1. Встроенные латентно-критичные интенты — всегда выигрывают, минуя плагины.
  2. Специфичность: более точное/длинное совпадение бьёт более общее.
  3. Тай-брейк: first-party > сторонний.
  4. Всё ещё неоднозначно → диздамбигуация: ассистент уточняет у пользователя («кого спросить — X или Y?») либо LLM-роутер выбирает по контексту.
  5. Явная адресация: «спроси у учёта расхода, сколько я проехал» — направляет в конкретный плагин.
  6. На регистрации пересечения детектируются и подсвечиваются (автору/при установке).

6. Биндинги и структура плагина

  • Rust SDK-крейт shturman-sdk — first-class: типобезопасные обёртки над D-Bus, манифест, helpers (чтение сигналов, регистрация интентов/UI). Другие языки — через D-Bus (язык-агностично), биндинги по мере нужды экосистемы.
  • Структура: каталог с manifest.yaml + исполняемый бинарь + ассеты.
  • Жизненный цикл (сторонние): discover → install (ревью capabilities) → App-Host поднимает в песочнице с прокси → run → update/remove. Дистрибуция/«магазин»/подпись — домен F.
  • First-party апы доставляются в составе подписанного read-only base-образа, НЕ проходят discover/install/ревью (auto-grant); якорь доверия — сам образ, не флажок (security-privacy §5).

7. Версионирование и совместимость

  • shturman_api: "N" — мажорная версия API; связана с набором D-Bus-мажоров интерфейсов (напр. "1"VehicleData1, IntentHandler1, Shell1…); связь фиксируется таблицей.
  • Параллельный рантайм: хост экспортирует на шине все поддерживаемые мажоры одновременно весь срок поддержки shturman_api — иначе «старые работают на своей» не подкреплено (ipc §2, аддитивно).
  • Депрекация: сколько мажоров хост держит одновременно и как анонсируется EOL старого.
  • Несовместимая версия: установка плагина с неподдерживаемым shturman_api явно падает ru.shturman.Error.Unsupported (не тихий запуск/деградация), всплывает в install-ревью.
  • SDK-крейт — semver.

8. Пример: fuel-tracker

Манифест выше. Что делает: подписывается на speed/maf/fuel_level/fuel_rate, считает пробег и средний расход (свой trip-стейт в storage), показывает тайл «Расход», отвечает на интенты «сколько я проехал»/«средний расход». Никакой сети, никакой записи — типичный безопасный плагин.


Открытые вопросы (найдено на self-review → роутинг)

  • 🟡 Глубина декларативного UI-DSL (что именно можно выразить тайлом/экраном без поверхности). → домен C (Shell).
  • ◻️ Дистрибуция, «магазин», подпись плагинов. → домен F.

Журнал решений (plugin-sdk)

Решение Выбор Дата
Манифест YAML; единый список capabilities (объединили с permissions) 2026-06-16
Точки расширения UI (тайлы/экраны, слот-модель) · интенты · vehicle_read · settings 2026-06-16
Коллизия интентов слоистая: built-in > специфичность > first-party > диздамбигуация; + явная адресация 2026-06-16
Сеть host-allowlist в манифесте; голый network: true — повышенный риск 2026-06-16
Биндинги Rust shturman-sdk first-class; др. языки через D-Bus 2026-06-16
Версии shturman_api ↔ набор D-Bus-мажоров; параллельный рантайм; неподдерж. → Unsupported 2026-06-16
Интенты HandleIntent с обязательным таймаутом; крэш/таймаут → деградированный ответ, не виснет 2026-06-16
Манифест-авторитет extension_points.tiles авторитетны; ui_tiles — квота (len ≤ N) 2026-06-16
Хранилище квота на /data/apps/<id> (ENOSPC плагину); remove чистит; миграция — плагин 2026-06-16