Scribbler
Run WebAssembly on the BEAM. Scribbler reads a .wasm module and hands it to
Carder, which turns it into a module the BEAM can load.
Where it fits
Rust compiles to WebAssembly, and so do Java and JavaScript with the right tools, so running WebAssembly on the BEAM lets Erlang, Elixir and Gleam call code written in any of them. Scribbler decodes and validates the module and lowers it to Carder's IR, and Carder does the rest.
Carder builds every module with its safe defaults, so code you did not write cannot crash the node or reach anything you did not give it.
Run a module
Scribbler builds with Gleam 1.16 on Erlang/OTP 29.
git clone https://github.com/scarletindustries/scribbler
cd scribbler
gleam run -- run test/scribbler/conformance/corpus/add.wasm add 3 5
That calls the add export of this module with 3 and 5, and prints 8:
(module
(func (export "add") (param i32 i32) (result i32)
(i32.add (local.get 0) (local.get 1)))
(func (export "mul") (param i32 i32) (result i32)
(i32.mul (local.get 0) (local.get 1))))
gleam run -- help lists every command.
Inside your program
A BEAM application that runs WebAssembly it has been given, such as a plugin, depends on
both packages. scribbler/embed compiles the bytes and
carder/embed runs them:
import carder/embed
import scribbler/embed as wasm
let assert Ok(compiled) = wasm.compile_named(bytes, "plugin")
let assert Ok(instance) = embed.instantiate(compiled, host)
let result = embed.invoke(instance, "add", [3, 5])
// -> Ok([8])
embed.stop(instance)
Give each guest a name of its own, because a node holds one module of each name and a second guest with the same name replaces the first.
Status
Scribbler is experimental. It runs the official WebAssembly spec suite and matches wasmtime bit for bit on 3 Rust crates, but the code it compiles runs slowly for now, so do not put it in production.