Files
viexp/docs/research/plugin-architecture.md

70 lines
4.2 KiB
Markdown

---
type: research
status: analyzed
confidence: high
created: 2026-10-17
question: How should viexp's file-type modules and plugins be designed so Linux users can disable, replace and extend them easily?
tags: [research, plugins, architecture, lua, wasm]
---
# Extension architecture research
## Mechanism comparison
| Mechanism | Safety | Ease for users | Host complexity | Hot reload | Proof |
|---|---|---|---|---|---|
| External process contract (argv/env/stdout) | low (= shell) | **highest** — any language | very low | free | nnn plugins, lf previewer, ranger scope.sh |
| Declarative rules (TOML / rifle / mailcap) | high (data, not code) | high — one-liners | low | free | rifle.conf, yazi `[plugin]` tables |
| In-process Lua (mlua) | medium — sandboxes leak (2025 CVE); Luau is the hardened dialect | medium | medium-high | yes | yazi (mlua 0.12, vendored Lua 5.5), wezterm, neovim |
| Rhai | high | low (niche language) | low-medium | yes | niche |
| JS (deno_core/V8) | medium-high | medium | high (~30 MB) | yes | Deno |
| WASM (wasmtime/extism) | **highest** | lowest (compile a component) | high (WIT churn) | yes | zellij |
| Host-language plugins (Python) | lowest (monkeypatching) | high for pythonistas | medium, fragile | poor | ranger |
## Case-study lessons
- **ranger**: the script pipeline (scope.sh + rifle) carried ~90% of real customization; the
Python layer exists because previews/commands need *state*. Three disjoint mechanisms = confusion.
- **nnn**: cleanest "process as API": `$1`=file, `$2`=cwd, `$3`=picker; context in env
(`NNN_FIFO` hover stream); control back via `NNN_PIPE` with tiny opcodes (`1c/etc` = cd ctx 1).
- **lf**: minimum viable preview contract = `(path, w, h, x, y)` + `cleaner` hook + exit-code
caching semantics. Document SIGPIPE/exit codes or users suffer.
- **yazi**: built-ins are preset plugins routed through the same `[plugin]` TOML tables users
edit (`prepend_*`/`append_*` merge) — one dispatch model for built-in and user code. mlua
with vendored Lua so users install nothing. Still BETA → in-process scripting is the most
expensive layer to stabilize.
- **neovim**: Lua won because the host API is rich, stable and uniform — half-exposed APIs are
worse than external scripts.
- **zellij**: WASM gives real sandboxing but locks architecture (single-threaded synchronous
plugins; WASIp2 migration "a significant undertaking"). Not worth it for v1.
- **kakoune**: `disabled_hooks` regex = cheap runtime kill-switch worth copying.
## Cargo feature flags = compile-time layer, not the primary switch
Features (`pdf`, `video`, … default-on) trim binary size and C deps, but feature unification
breaks packagers, combinations explode, and removing a default feature is a breaking change.
**Runtime `enabled = false` in modules config must remain the real disable mechanism**; every
`#[cfg]`-gated module should degrade to a TOML rule pointing at an external tool.
## Recommended phasing (adopted)
- **v0.1 — declarative core**: every file-type module = rule in a TOML registry mapping
mime/glob → previewer/opener, with per-module `enabled` switch and `prepend_/append_` merge.
Built-ins already dispatch through this table → replacing/reordering needs no recompile.
- **v0.2 — external process protocol**: freeze the script contract —
`argv = (path, width, height, x, y)`, env context, FIFO/socket write-back, `cleaner` hook.
Ship bash + Python reference plugins. This is where 90% of Linux-user extensibility lives.
- **v1.0+ — embedded Lua** (mlua, vendored): only for what scripts cannot do — stateful,
in-process previews/fetchers — registered through the same tables. Evaluate Luau if
sandboxing becomes a requirement. WASM explicitly rejected for v1.
## Outcome
Adopted as [[decisions/ADR-002-extension-architecture|ADR-002]] and [[decisions/ADR-003-file-type-modules|ADR-003]].
## Sources
- yazi docs (plugins overview, configuration), yazi Cargo.tomls (mlua 0.12 lua55 vendored)
- nnn plugins README; lf doc.md; rifle(1); RFC 1524 (mailcap)
- zellij plugin docs + discussion #4139; wezterm config docs; neovim lua.txt; kakoune FAQ
- mlua/rhai/gluon repos; deno blog; Lua sandbox-bypass CVE coverage (2025)
- Cargo book: features.md