WASM plugins support hot-reload without server restart:
cargo build --target wasm32-wasip1 --releasecp target/wasm32-wasip1/release/*.wasm ~/.signalk/...During reload:
stop() is called on old instancestart() is called with saved configIf a WASM plugin crashes:
Report errors to admin UI:
fn handle_error(err: &str) {
sk_set_error(&format!("Error: {}", err));
}
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Enable link-time optimization
strip = true # Strip debug symbols
Use wasm-opt for further optimization:
wasm-opt -Oz plugin.wasm -o plugin.wasm
fn start(config_ptr: *const u8, config_len: usize) -> i32 {
match initialize_plugin(config_ptr, config_len) {
Ok(_) => {
sk_set_status("Started");
0 // Success
}
Err(e) => {
sk_set_error(&format!("Failed to start: {}", e));
1 // Error
}
}
}
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct Config {
#[serde(default)]
enabled: bool,
}
fn parse_config(json: &str) -> Result<Config, serde_json::Error> {
serde_json::from_str(json)
}
WASM plugins running in Node.js have ~64KB buffer limitations for stdin/stdout operations. This is a fundamental limitation of the Node.js WASI implementation, not a Signal K restriction.
Impact:
Hybrid Architecture Pattern
For plugins that need to handle large data volumes (logs, file streaming, large JSON responses), use a hybrid approach:
Use this pattern when your plugin needs to:
fn debug_log(message: &str) {
unsafe {
sk_debug(message.as_ptr(), message.len());
}
}
cargo build --target wasm32-wasip1wasmtime for local testing:wasmtime --dir /tmp::/ plugin.wasm
# Linux/macOS
DEBUG=signalk:wasm:* signalk-server
Issue: Plugin doesn't load
Solution: Check wasmManifest path in package.json
Issue: Capability errors Solution: Ensure required capabilities declared in package.json
Issue: Crashes on start Solution: Check server logs for error details
Check if your plugin:
Convert TypeScript/JavaScript logic to Rust:
Before (Node.js):
plugin.start = function (config) {
app.handleMessage('my-plugin', {
updates: [{ values: [{ path: 'foo', value: 'bar' }] }]
})
}
After (WASM/Rust):
fn start(config_ptr: *const u8, config_len: usize) -> i32 {
let delta = json!({
"updates": [{ "values": [{ "path": "foo", "value": "bar" }] }]
});
sk_emit_delta(&delta.to_string());
0
}
Use migration helper to copy existing data to VFS:
fn first_run_migration() {
// Server provides migration API
// Copies files from ~/.signalk/plugin-config-data/{id}/
// to ~/.signalk/plugin-config-data/{id}/vfs/data/
}
The following example plugins are available in the repository: