A native Extism host SDK for Erlang/OTP. Plugins run on erlang_wasm. This library implements the Extism kernel, host env, manifests, host functions and WASI on that engine.
Requires OTP 29+.
The default handle lives in your process. No gen_server, no mailbox hop.
{ok, P} = extism:open("priv/plugins/count_vowels.wasm"),
{ok, Out} = extism:call(P, count_vowels, <<"Hello, World!">>),
ok = extism:close(P).
%% Out = <<"{\"count\":3,\"total\":3,\"vowels\":\"aeiouAEIOU\"}">>new/1 is the same as open/1. A path, raw bytes, WAT, a bundled name, or a
manifest are all fine:
{ok, P} = extism:open(#{
wasm => [#{path => "plugin.wasm"}],
config => #{<<"vowels">> => <<"aeiou">>},
allowed_hosts => [<<"example.com">>]
}).A map of funs. Arity is the number of Extism memory handles; values are
binaries. That is the usual PDK shape (String / Vec<u8>).
{ok, P} = extism:open(count_vowels_kvstore, #{
functions => #{
kv_read => fun(Key) -> {ok, maps:get(Key, Store, <<0,0,0,0>>)} end,
kv_write => fun(Key, Val) -> ets:insert(T, {Key, Val}), ok end
}
}).When you need the handle (or raw i64 offsets):
#{host_reflect => {raw, [i64], [i64], fun(P, [Off]) ->
{ok, In} = extism:read(P, Off),
extism:write(P, In)
end}}Own the instance from a worker when you want a name, a supervisor, or to kill a runaway call by stopping the process.
{ok, Pid} = extism:start_link(Source, #{name => {local, vowels}}),
{ok, Out} = extism:call(Pid, count_vowels, <<"hi">>),
%% in your supervisor:
extism:child_spec(Source, Opts, #{id => vowels}).call/3 accepts either the inline handle or the pid.
Every failure is one map:
{error, #{class := plugin | link | trap | host | timeout | manifest,
reason := atom(),
message := binary()}}priv/plugins/ has real guests from Extism PDK and SDK repos. See
priv/plugins/SOURCES.md.
extism:bundled_plugins().rebar3 compile
rebar3 ct
rebar3 shell