- data-model: структура DTC-кода исправлена (буква + 2-битная категория + 3 hex, не «4 hex»; P0/P2 generic, P1/P3000-33FF производитель); module_voltage путь → Vehicle.OBD.ControlModuleVoltage (был неверный LowVoltageBattery) - plugin-sdk: пример id → dev.example.* (ru.shturman.* зарезервирован); i18n строк манифеста (per-locale, ru обязателен); ui_screens capability + гейт RegisterScreen - hardware: камера «фаза 2» → v2; требования к MCU-копилоту (обновляемость прошивки, fail-safe при его отказе, позиция в цепочке доверия) - tech-stack: карты «фаза 2» → v4 (нормализация фазовой номенклатуры) - C-shell: день/ночь v0 = время, GPS-восход v1 (был конфликт фаз); ui_screens-гейтинг Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
14 KiB
Контракт: 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: dev.example.fuel-tracker # reverse-DNS своего домена; ru.shturman.* зарезервирован за first-party (security-privacy §5)
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
ui_screens: 1
storage: true
# network: { hosts: ["api.example.ru"] } # опц.: host-allowlist (см. §3)
extension_points:
tiles:
- id: consumption
title: "Расход"
intents:
handler: /dev/example/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.
i18n (принцип #10): пользовательские строки (name/description/tiles[].title/
assistant_intents) — локализуемы: per-locale секции (assistant_intents: { ru: […], en: […] })
или ключи + locales/<lang>.yaml. ru обязателен (дефолт + фолбэк); матчинг интентов — по
активной локали (d-assistant §6). На старте заполняем только ru (i18n-ready, не i18n-now).
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, строго работает только бинарныйnetworkon/off (security-privacy §3/§7). Голыйnetwork: true+vehicle_read/location— повышенный риск при установке.
4. Точки расширения
4.1 UI — тайлы и экраны (слот-модель, решение №6)
Shell.RegisterTile(decl)— декларативный элемент (рендерит shell в едином стиле).Shell.RegisterScreen(decl) → slot_token(требует capabilityui_screens) — декларативный экран или Wayland-поверхность (slot-token + slot-протокол, c-shell §4).- Декларативный словарь (констрейнт):
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_read→VehicleData.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. Разрешение коллизии интентов (резолв открытого вопроса)
Когда два плагина регистрируют пересекающиеся фразы — слоистая политика:
- Встроенные латентно-критичные интенты — всегда выигрывают, минуя плагины.
- Специфичность: более точное/длинное совпадение бьёт более общее.
- Тай-брейк: first-party > сторонний.
- Всё ещё неоднозначно → диздамбигуация: ассистент уточняет у пользователя («кого спросить — X или Y?») либо LLM-роутер выбирает по контексту.
- Явная адресация: «спроси у учёта расхода, сколько я проехал» — направляет в конкретный плагин.
- На регистрации пересечения детектируются и подсвечиваются (автору/при установке).
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 |
| i18n | строки манифеста локализуемы (per-locale/ключи); ru обязателен (i18n-ready) |
2026-06-16 |
| UI-capabilities | ui_tiles (квота тайлов) + ui_screens (гейт RegisterScreen) |
2026-06-16 |