n7n-plugin-host 0.2.0

wasmtime-бекенд для crate::registry::PluginRegistry (n7n-harness) — друга (wasm-компонентна) реалізація слотів detector/verify/ast_facts/t0, поруч з in-process-бекендом §3.12 пункту 16а
Documentation

n7n-plugin-host

wasmtime-бекенд для harness::registry::PluginRegistry (n7n-harness) — друга (wasm-компонентна) реалізація чотирьох слотів плагінів (detector/verify/ ast_facts/t0), поруч з in-process-бекендом. Модульний шлях — plugin_host::... ([lib] name = "plugin_host"). Контракти — WIT-світи wit/*.wit (n7n:fix-deps@0.1.0), вендоровані всередині цього крейта (wasmtime::component::bindgen! читає .wit-файли на своєму боці компіляції, тож шлях мусить лишатись у межах опублікованого пакета — інакше build падає і на docs.rs, і для будь-кого, хто бере крейт з crates.io поза цим workspace).

Встановлення

У межах цього workspace крейт додається як звичайна path-залежність:

[dependencies]
plugin-host = { package = "n7n-plugin-host", version = "0.1", path = "../plugin-host" }

Модульний шлях лишається plugin_host::... незалежно від того, як названо package.

Приклад

WasmDetector::from_file компілює .wasm-компонент; into_detect_fn віддає той самий callable-тип (DetectFn), що harness::registry::PluginRegistry уже приймає від in-process-бекенда:

use plugin_host::WasmDetector;

let detector = WasmDetector::from_file(repo_root, "detector.wasm")?;
registry.replace_detector(detector.into_detect_fn());

Навіщо окремий крейт

wasmtime — найбільша залежність усього workspace-у. n7n-harness тримає інваріант «нуль важких залежностей» (CI перевіряє cargo tree -p n7n-harness --all-features — там немає wasmtime), а n7n-llm-lib (featureless-ядро) свідомо звужений. Жоден з них цей крейт не чіпає — він лише споживає обидва (n7n-harness за типами DetectFn/T0Fn/Violation/ VerifyFn/AstFactsFn, n7n-llm-lib з default-features = false за EditPlan/ VerifyReport). Підключає його лише той споживач, що реально вантажить wasm-плагіни — harness/llm-lib лишаються придатними для решти без жодного зайвого байта в графі.

Що всередині

Чотири типи — [WasmDetector], [WasmVerify], [WasmAstFacts], [WasmT0] — кожен виробляє рівно той самий callable-тип, що in-process-бекенд уже приймає в PluginRegistry::new/replace_*: реєстр не відрізняє, звідки прийшло замикання, з Arc::new(|| ...) напряму чи з WasmDetector::into_detect_fn().

Безпекова межа

  • Guest-и не отримують WASI-права запису. Єдине місце, де guest отримує (чи не отримує) права на диск — wasi_ctx::no_filesystem_ctx: жодного preopen, тобто файлової capability немає взагалі. Читає guest вужчим host-fs.read-file (§3.9 передбачає цей шлях явно).
  • T0 лише рахує план, не застосовує. WasmT0::into_t0_fn виробляє лише T0Fn (порахувати edit-plan) — фаза застосування (write_guard) сюди не входить за побудовою: n7n-llm-lib тут узятий з default-features = false.
  • Жодного спавну процесів у guest. Жоден із чотирьох WIT-світів не імпортує нічого, що дало б guest-у спавнити процес — WASI Preview 2 в принципі не має такого інтерфейсу.
  • Інстанс на спробу, без кешу. Кожен Wasm*::call* будує свіжий Store+WasiCtx+ Instance на кожен виклик; кешується лише скомпільований ComponentEngine/ Linker, які самі по собі не несуть стану guest-а).

Фічі wasmtime

Свідомо мінімальний набір, без дефолтних (wasmtime за замовчуванням тягне ще ~20 фіч — cache, GC-варіанти, profiling, pooling-allocator, threads тощо, з яких жодна тут не потрібна): runtime, component-model, cranelift (єдиний доступний компілятор без альтернативи winch). wasmtime-wasi — лише фіча p2 (Component Model WASI); p1 (preview1-адаптер) не потрібна, жоден guest не цілить wasm32-wasip1.

Тести

tests/guest/detector_fixture і tests/guest/t0_fixture — мінімальні wasm32-wasip3- компоненти для наскрізного тесту (tests/wasmtime_backend.rs). Компіляція гейтована наявністю таргета в rustup target list --installed — де його немає, тест друкує причину й виходить рано, не падаючи.

MSRV

rust-version = "1.93" у Cargo.toml (workspace-успадкований). CI компілює на поточному stable, який завжди новіший.