WASM plugin
Plugins can be written in WebAssembly (WASM), a portable binary format. This means that plugins can be written in any language that compiles to WASM, like Rust, C, C++, Go, TypeScript, and more. Because WASM based plugins are powered by a programming language, they implicitly support complex business logic and behavior, have access to a sandboxed file system (via WASI), can execute child processes, and much more.
Since our WASM plugin implementation is still experimental, expect breaking changes to occur in non-major releases.
Concepts
Before we begin, let's talk about a few concepts that are critical to WASM and our plugin system.
Virtual paths
WASM by default does not have access to the host file system, but through WASI, we can provide sandboxed access to a pre-defined list of allowed directories. We call this virtual paths, and all paths provided via function input or context use them.
Virtual paths are implemented by mapping a real path (host machine) to a virtual path (guest runtime) using file path prefixes. The following prefixes are currently supported:
Real path | Virtual path |
---|---|
~ | /userhome |
~/.proto | /proto |
CWD | /workspace |
For example, from the context of WASM, you may have a virtual path of /proto/tools/node/1.2.3
,
which simply maps back to ~/.proto/tools/node/1.2.3
on the host machine. However, this should
almost always be transparent to you, the developer, and to end users.
However, there may be a few cases where you need access to the real path from WASM, for example,
logging or executing commands. For this, the real path can be accessed with the
real_path
function on the VirtualPath
enum (this is a Rust only feature).
input.tool_dir.real_path();
Host environment
Since WASM executes in its own runtime, it does not have access to the current host operating
system, architecture, so on and so forth. To bridge this gap, we provide the
get_proto_environment
function.
Learn more about this type.
let env = get_proto_environment()?;
The host operating system and architecture can be accessed with os
and arch
fields respectively.
Both fields are an enum in Rust, or a string in other languages.
if env.os == HostOS::Windows {
// Windows only
}
if env.arch == HostArch::Arm64 {
// aarch64 only
}
Furthermore, the user's home directory (~
) and proto's root directory (~/.proto
) can be accessed
with the home_dir
and proto_dir
fields, both of which are virtual paths.
if env.home_dir.join(some_path).exists() {
// Do something
}
Host functions
WASM is pretty powerful but it can't do everything since it's sandboxed. To work around this, we provide a mechanism known as host functions, which are functions that are implemented on the host (in Rust), and can be executed from WASM. The following host functions are currently available:
exec_command
- Execute a system command on the host machine, with a provided list of arguments or environment variables.get_env_var
- Get an environment variable value from the host environment.host_log
- Log a message to the host's stderr. This acts like tracing logs, and is not a general purpose stdout logger.set_env_var
- Set an environment variable to the host environment.
To use host functions, you'll need to make them available by registering them at the top of your Rust file (only add the functions you want to use).
#[host_fn]
extern "ExtismHost" {
fn exec_command(input: Json<ExecCommandInput>) -> Json<ExecCommandOutput>;
fn get_env_var(key: &str) -> String;
fn host_log(input: Json<HostLogInput>);
fn set_env_var(key: &str, value: &str);
}