You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The plugin host infrastructure is implemented and tested: wasmtime sandbox,
manifest parsing, capability gating, HostApi imports, fuel budget, and epoch
timeout isolation (plugin-host + log-hello / busy-loop examples).
No product content plugins have been built yet (allemannsretten camping-spot
spec, marine traffic, weather overlays, bathymetry, sonar, RTL-SDR/APRS-adjacent
guests, UI translation packs, animated icon packs, etc.). That is expected and intentional, not a bug or an oversight —
the host exists so future contributors can ship those plugins independently
without changing the navigation core. Country/region-dependent packs (camping,
future driving-hour families, horse access, …) follow the reusable pattern in
jurisdiction-rules.md. Product requirements for
the system (not optional): users can enable/disable each plugin, and
hardware-facing plugins can talk over USB and Bluetooth via the host —
see Enable / disable and
External device I/O. See
Ideas for beneficial plugins
below as an open invitation / roadmap, not as incomplete core features.
Navi runs untrusted extension code in a wasmtime sandbox (plugin-host), not
in the routing / sensor / UI process address space as native code.
Guest plugins compile to wasm32-unknown-unknown and call a narrow HostApi via
WASM imports. WASI filesystem and network are not linked — only the
capabilities declared in the plugin manifest are wired. Host-owned code (Android
service, native accessory) may open USB/serial/network and feed sanitized
snapshots into the core; WASM guests must not get raw sockets.
Gate: wasmtime Android product link (Phase 5a)
Source of truth for this gate.docs/status.md only points here.
Current state (2026-09 Phase 5a)
plugin-host pins wasmtime major 48 (lockfile 48.0.3), with
features cranelift + runtime only (default-features = false; no Winch,
WASI, component-model, wat, cache, profiling, coredump, addr2line/demangle,
parallel-compilation, or GC collectors — guests do not use Wasm GC).
navi-ffi (Android libnavi.so) links navi-plugin-host. The right-to-roam
camping guest runs under Cranelift JIT on shipped ABIs (arm64-v8a, x86_64).
navi-desktop / navi-linux still must not depend on navi-plugin-host
(guarded by scripts/check-plugin-host-gate.sh).
Per-call isolation: fuel + epoch wall-clock + linear-memory ceiling
(StoreLimits, default 16 MiB; camping guest 32 MiB). Trap / timeout / fuel /
memory → plugin disabled for the session with a visible message; app never crashes.
Guest .wasm is built from source via scripts/build-plugin-wasm.sh into
app/src/main/assets/plugins/ (gitignored). No committed binaries; F-Droid recipe
adds wasm32-unknown-unknown and runs the same script before assemble.
Runtime choice: Cranelift JIT from .wasm at load (Module::from_file). No
AOT .cwasm in the APK (F-Droid simplicity; avoids ABI-specific precompile blobs).
Pulley is not required for shipped ABIs; any future unsupported ABI fails closed
(PluginError::UnsupportedAbi / plugin unavailable — never crash).
The wasmtime 29-era RustSec ignores remain cleared fromdeny.toml. Example-guest wee_alloc ignore remains for fixtures.
Feature / backend confirmation:cargo tree -p navi-plugin-host -e features -i wasmtime shows only cranelift and runtime (plus transitive std /
unwinder / icache-coherence those enable). Only plugin-host
declares wasmtime in the workspace.
Android aarch64 verification (kept green): CI
plugin-host-android-aarch64 / scripts/plugin-host-android-aarch64-smoke.sh
(NDK link + QEMU aarch64 Cranelift). Bionic on-device smoke was verified 2026-08-29
on SM-P613; re-run --adb after fuel/epoch/memory embedder changes.
Required before linking other product hosts
Keep the aarch64 smoke green on the wasmtime pin you ship.
Re-run scripts/check-plugin-host-gate.sh after any wasmtime or workspace
dependency change.
Before linking navi-desktop / navi-linux, wire the same HostApi + enable-store
path and remove those crates from the premature-link guard in the gate script.
IMU guest (pitch / tilt / compass, mount calibration, display) on sensors_read; engine in plugins/imu/engine; staged into the APK by scripts/build-plugin-wasm.sh and fed by the Android SensorManager backend via navi-ffi (imu_plugin_*), default OFF (plugins/imu-plugin.md)
Capabilities are validated before the module is instantiated. Unknown
capability names reject the load. Requested capabilities must be a subset of the
host policy set passed to PluginHost::load_dir.
Enable / disable (required)
Every installed plugin must be individually enableable and disableable by
the user. This is a product requirement for the plugin system, not an optional
per-plugin nicety.
Rule
Detail
Per-plugin toggle
Host UI (Tools / Settings) shows one on/off control per installed plugin (manifest name).
Persisted
Enabled/disabled state is stored on device (e.g. plugin_kv / host preferences). Survives app restart.
Default
Newly installed plugins start disabled until the user turns them on (opt-in), unless a ship checklist documents an exception.
Disabled = not called
A disabled plugin is not invoked on route plan, tick, or event hooks. Its HostApi imports must not run. Capability grants alone do not keep it live.
Core without plugins
Routing, map, GPS, and HUD must keep working with all plugins disabled (Design rules).
Load vs enable
The host may still discover / validate a .wasm + plugin.json while disabled; enable only gates execution and side effects (audio, overlays, accessory I/O).
Future host API sketch (not in ABI yet): plugin_list / plugin_set_enabled(name, bool)
owned by the Android/desktop host, not by guest WASM.
External device I/O — USB and Bluetooth (required)
Plugins that talk to hardware must be able to use USB and/or Bluetooth
(including BLE where the device requires it). Guests never open raw sockets or
raw HID/serial themselves — the host owns the transport and feeds sanitized
data (or accepts sanitized commands) through capability-gated HostApi imports.
Rule
Detail
Transports
USB (USB-serial, USB accessory / AOA, wired UART over USB) and Bluetooth (Classic SPP and/or BLE GATT) are first-class plugin I/O paths.
Host-mediated
WASM guests request open/read/write/close via HostApi. Host opens the OS device, enforces permissions, rate-limits, and may disconnect on disable / app background policy.
User consent
Pairing, USB permission dialogs, and “allow this plugin to use accessory X” stay in the host UI. Disabling a plugin closes its sessions.
No silent TX
Radio CAT, OBD write, or any transmit path stays gated (read/query free where safe; TX only with explicit arm — see CAT / ECU ideas below).
Offline routing
Accessory I/O must not block T2 UI or A*. Dropped links degrade the plugin feature, not core nav.
Proposed capabilities (not in ABI yet)
Proposed
Purpose
accessory_list
List paired / attached USB and Bluetooth devices the host is willing to expose
accessory_open
Open a session (plugin id + device id + mode: usb-serial / bt-spp / ble)
accessory_read / accessory_write
Exchange bytes or framed messages; host may impose max length / rate
accessory_close
Release the session (also implied when the plugin is disabled)
accessory_events
Optional push of connect/disconnect / permission-denied
Until those land, host-native services (as already sketched for ECU Bluetooth /
USB, CAT via Hamlib — see CAT.md — and DIY e-bike USB-serial) may open
the link and push snapshots into core; a future WASM guest then only calls
ecu_read / cat_vfo_set / ebike_telemetry_read-class imports.
Specs that need a cable or radio (ECU, CAT, ebike_telemetry,
lora_convoy, APRS TNC, future instruments) must document which
transport(s) they use and that they honour enable/disable (sessions closed
when off).
Debug files (USB/MTP)
On-device diagnostic and debug artifacts must be USB/MTP-retrievable without
adb, under the same shared tree as the core diagnostic session log
(debugging.md):
Use a stable folder name matching the plugin manifest name (lowercase,
hyphen or underscore — same string users will see on USB).
Prefer Documents/debug/<plugin-name>/. If the host falls back to
Download/debug for the core log, plugins must use that same root’s
debug/<plugin-name>/ tree.
Do not write plugin debug files into app-private storage only, into the
core navi_session_*.log file, or into a top-level /debug folder (not
creatable without MANAGE_EXTERNAL_STORAGE).
The current log HostApi import goes to the host logger (logcat / host
process); durable USB-visible files need a future host capability (or
host-owned code writing into Documents/debug/<plugin-name>/ on the
guest’s behalf). Specs that mention “diagnostics” should target this layout.
A plugin that busy-loops is terminated with CallOutcome::FuelExhausted or
CallOutcome::Timeout. Isolation is covered by
plugin-host/tests/isolation.rs (load→call→return for log-hello; kill +
host-thread heartbeat for busy-loop).
Copy the produced .wasm next to plugin.json as plugin.wasm, then:
cargo test -p navi-plugin-host --test isolation -- --nocapture
Ideas for beneficial plugins
None of the plugins below are shipped yet. They are the intended extension
surface: each should stay sandboxed (or host-side with a thin capability), push
normalized data into core stores, and never starve T2 UI/audio. Protocol and
DSP docs already exist where noted.
1. APRS (aprs / aprs_sdr)
Benefit
Live tactical positions, weather beacons, short messages on map
SDRoxide issue #150 — Navi asked about an APRS plugin; upstream is considering APRS as built-in with the decoder in its own crate, which this plugin could reuse rather than duplicating AFSK/AX.25
Range display already clamps to 50–150 km in core — plugins must respect
that (no global dump onto the map).
2. Weather (weather)
Benefit
Current conditions / alerts along route (wind, precip, temp, pressure)
National road authorities, DATEX-II style feeds, OSM notes/highway=* diffs, user reports — always opt-in network
Research
plugins/traffic-information.md — why a free / global / ~1-minute source does not exist today; RTL-SDR RDS-TMC / DAB-TPEG alternative under consideration
NPRA DATEX (via navi-server)
plugins/datex-plugin.md — host GET of cached /datex/npra/GetSituation.xml (default OFF; no NPRA credentials on device)
navi-cat + navi-hamlib-sys + USB/BT loopback / remote rigctld; never auto-TX (rig_get_ptt interlock; never rig_set_ptt / T); enforce gating + read-back even if the guest misbehaves; close sessions on plugin disable
Read/query free; TX inhibited; program only when PTT clear and read-back matches
Auto-tune summary (full detail in CAT.md): if a NFM amateur repeater is within
150 km, resolve output frequency, shift/offset, and CTCSS/DCS, then program
VFO 1 only.
Same client/display split as the planned LoRa convoy plugin (Navi does not
implement the RF/mesh layer): plugins/lora-convoy-spec.md.
5. ECU / EV telemetry (ecu)
Benefit
Live fuel rate / SoC / power for eco reweight and range UI
Live assist power, pack SoC, regen, and DIY-computed remaining time/distance for Electric Cycle — supplements physics-based EbikeConfig estimates when a custom display/BMS is cabled in
Docs
ebike-telemetry-diy.md — open $NAVIPWR ASCII over USB-serial (CAN optional); no open cross-vendor standard exists
Transport
Wired USB-serial primary; CAN secondary; Bluetooth/BLE allowed when the DIY display/BMS exposes it — same host-mediated accessory_* path as ECU
Host duties
Open UART/CAN/BT; parse checksummed $NAVIPWR; expose latest snapshot (WASM cannot open serial); close on plugin disable
Recorded clips are the only audio path (authored offline; English fallback if a language pack is missing). Spoken guidance is a legitimate foreground audio interruption (unlike silent background routing). Per-language concat vs whole-phrase clips is an open design question.
Spec only — not implemented. Informational guidance, not legal advice. No Nordic wild-camp logic for England/Wales or Denmark. Country packs and “decline rather than guess” are the reference example in jurisdiction-rules.md.
Before departure, warn when the largest gap between reliable fuel or water stops exceeds usable range (tank/load minus conservative buffer), with stricter buffers in remote/arid terrain
Spec only — not implemented. Distinct from core SafetyConfig (POI radii / overnight distances). Conservative guidance, not a supply guarantee.
9. Instrument cluster / AGL signal export (instrument_cluster)
Benefit
Export Navi nav state (speed, limit, overspeed chrome, next maneuver, ETA, break timing, eco, polyline, and merged approach warnings — road signs, children-zone proximity, speed cameras, seasonal closures) to open-source clusters and AGL via VSS/Kuksa.val, with a simple JSON fallback
Assemble guidance + merged warning snapshot (same UniFFI / merge order as map chrome); implement Kuksa.val / loopback JSON / log sinks; user opt-in; never grant raw sockets to WASM
Guest duties
Decide what/when to publish; call vehicle_signal_publish; enforce no-route clear for rest fields and clear warning leaves when chrome is hidden
Spec only — not implemented. Today the app UI is English only and has no language toggle. Do not infer UI language from GPS/SIM. Parallel markdown (docs/Norwegian.md, etc.) is documentation, not in-app i18n.
11. Animated icons (animated_icons / icon_anim)
Benefit
Synfig-authored motion for HUD / selected markers without putting a Synfig runtime in the routing core
Spec only — not implemented. Core still renders one SVG per rasterize_key call. Road-sign catalogue (no_sign_*) remains static NLOD art unless an optional anim overlay is added later.
Spec only — not implemented. One short tone at urgency phase entry (750/150/25 m model), not continuous audio through the approach window.
13. Horse trekking (horse_trekking / horse_trek)
Benefit
Horse-scale water and support-service lookahead, per-protected-area access guidance, and field notes for equestrian trekking — while core Horse routing preference stays a soft cost model (prefer unpaved / bridleway / route=horse, town-bypass penalty)
Escalating spoken overspeed alerts by percentage over the HUD’s applicable limit, with a long arm delay so routine overtakes do not nag, and faster disarm when the driver corrects
Spec only — not implemented. Motor profiles only. Not a ticket predictor. HUD chrome stays display-only.
15. LoRa convoy status (lora_convoy / convoy)
Benefit
Share location, speed, fuel, and vehicle battery charge across a convoy over a LoRa mesh so low fuel is visible to the rest of the group without requiring direct radio range
Docs
plugins/lora-convoy-spec.md — Meshtastic client/display split (same shape as CAT), private-portnum VehicleStatus, two independent BLE sessions (radio vs Android companion), seq/stale table
Host duties
BLE central to a Meshtastic-flashed node (meshtastic crate, bluetooth-le + tokio); separate BLE session for passenger Android GATT writes; encode/decode; convoy_store; overlay + warn thresholds; close both sessions on disable
Guest duties
Optional: chip formatting / list sort after wasmtime gate; must not open BLE
Driver does not enter fuel/battery on the Navi device while moving — companion is passenger-operable. Mesh TX only while the plugin is enabled. Informational overlay, not collision avoidance.
Notes
Spec only — not implemented. Navi never talks to raw LoRa modules. Recommended radio: Meshstick USB SX1262 TCXO stick (USB head units; unique identity, traceability, flat mount face) or BLE Meshtastic boards (tablets). Text messaging is a later arm on the same PortNum dispatch.
16. POI look-ahead cone (poi_lookahead / poi_cone)
Benefit
Quiet "worth a look" discovery of attractions, fishing spots, craft breweries, and cider makers in a heading-driven cone ahead of the vehicle (with or without a route) — not a hazard warning
Docs
plugins/poi-lookahead-cone-spec.md — 850 m / ±30° cone, General + Fishing + CraftBrewery filter, open-now opening_hours gate, separate from the 300 m hazard cone
Host duties
Cone or radius+bearing POI query; evaluate or expose open-now; render quiet chips/markers; enable/disable
Guest duties
Category filter, cone membership if host returns a radius set, hours policy, nearest-first list — no audio
Host-native UniFFI + quiet HUD chip; WASM guest scaffold. Must suppress known-closed venues. Does not modify live_hazard.rs. brewery=cider is classified as CraftBrewery in core. Master toggle default OFF.
Capability sketch (not in ABI yet)
Proposed
Purpose
track_upsert
Push APRS / track stations into host TrackStore
weather_read
Read cached weather samples near lat/lon
incident_query / incident_write
Road closures / convoy / accident overlays
repeater_query
Nearest NFM repeaters from onboard DB (+ optional RepeaterBook sync)
cat_vfo_set
Ask host to program VFO 1 (frequency, offset, tone)
Host-owned inventory and per-plugin enable/disable (UI + persistence)
Add a capability to plugin-hostCapability enum + HostApi before shipping
any guest that needs it. Until then, host-native services may write into core
via UniFFI without WASM.
Design rules for all plugins
Wasmtime ship gate:navi-ffi (Android) links plugin-host as of Phase 5a
(Cranelift JIT, fuel+epoch+memory). Keep aarch64 smoke green and re-run
scripts/check-plugin-host-gate.sh after wasmtime changes. Do not link
navi-desktop / navi-linux until those hosts wire the same path
(gate section).
The crate pin is 48.0.3 with Cranelift-only features confirmed.
Offline-first: network is opt-in; core routing must work with plugins
disabled.
Enable / disable: every plugin has a user-facing on/off control; disabled
plugins are not executed and must release USB/Bluetooth sessions
(Enable / disable).
USB / Bluetooth: hardware-facing plugins use host-mediated USB and/or
Bluetooth — never raw guest sockets
(External device I/O).
No silent map-data mutation: OSM extracts and graph caches change only via
explicit user actions (osm-updates.md).
Tier priorities: plugins run at T0/T1 priority budgets — never block UI.
Privacy: no VIN / callsign / location upload unless the user enables it.
Range discipline: map overlays respect the same ~150 km class of filters
used by tracks and CAT repeater search unless the user raises a documented
clamp.