Quickstart
The following is a minimal guide to running writing an Extism plug-in in your language
of choice. This document should get you from 0
to "Hello, World!"
as quickly as possible.
Choose A Language
In Extism, your plug-in code is compiled to a binary Wasm module. We offer a variety of libraries, which we call PDKs (Plug-in Development Kits), to help you compile your code to a Wasm module that can be embedded in any Host SDK.
First choose a language for your plug-in:
- Rust
- JavaScript
- Go
- C#
- F#
- C
- Haskell
- Zig
- AssemblyScript
Install the Dependency
Generate a lib
project with Cargo:
cargo new --lib my-plugin
Add the library from crates.io.
cargo add extism-pdk
Change your Cargo.toml
to set the crate-type to cdylib
(this instructs the compiler to produce a dynamic library, which for our target will be a Wasm binary):
[lib]
crate_type = ["cdylib"]
Create an Export
The primary means of interacting with this plug-in is an export function that can be called by the outside world.
Let's create a simple greeting plug-in. The plugin_fn
macro wires this function to Extism and exports it
using the name greet
:
use extism_pdk::*;
#[plugin_fn]
pub fn greet(name: String) -> FnResult<String> {
Ok(format!("Hello, {}!", name))
}
Compile the Plug-in
We can compile to the wasm32-unknown-unknown
since we don't need to make any syscalls (not using WASI):
cargo build --target wasm32-unknown-unknown
If this fails, make sure you have added this target to rust:
rustup target add wasm32-unknown-unknown
Running the Plug-In
This will put your compiled wasm in target/wasm32-unknown-unknown/debug
.
We can now test it using the Extism CLI's run
command:
extism call target/wasm32-unknown-unknown/debug/my_plugin.wasm greet --input "Benjamin"
# => Hello, Benjamin!
You will get better performance and smaller .wasm
binaries if you build your code using --release
.
cargo build --target wasm32-unknown-unknown --release
Documentation
Congrats! You just wrote your first Extism plug-in! To learn more about what this rust library can do, see the rust-pdk README and reference docs.
Install the Dependency
First install the Extism JS compiler with this install script:
curl -O https://raw.githubusercontent.com/extism/js-pdk/main/install.sh
sh install.sh
Then run command with no args to see the help:
extism-js
error: The following required arguments were not provided:
<input-js>
USAGE:
extism-js <input-js> -i <interface-file> -o <output>
For more information try --help
If you are using mac, you may need to tell your security system this unsigned binary is fine.
Create an Export
The primary means of interacting with this plug-in is an export function that can be called by the outside world. Let's create a simple greeting plug-in. Just like a normal JS module, any function we export will be accessible to the outside world:
function greet() {
const name = Host.inputString()
Host.outputString(`Hello, ${name}`)
}
module.exports = {greet}
We must also describe the Wasm interface for our plug-in. We do this with a typescript module DTS file. Here is our plugin.d.ts file:
declare module 'main' {
// Extism exports take no params and return an I32
export function greet(): I32;
}
Compile the Plug-in
Now we must compile this plug-in to wasm:
extism-js plugin.js -i plugin.d.ts -o plugin.wasm
Running the Plug-In
We can now test it using the Extism CLI's run
command:
extism call plugin.wasm greet --input "Benjamin" --wasi
# => Hello, Benjamin!
At this time, all js plug-ins need WASI to run
Documentation
Congrats! You just wrote your first Extism plug-in! To learn more about what this js library can do, see the js-pdk README.
Install the Dependency
Include the library with Go get:
go get github.com/extism/go-pdk
Create an Export
The primary means of interacting with this plug-in is an export function that can be called by the outside world. Let's create a simple greeting plug-in.
package main
import (
"github.com/extism/go-pdk"
)
//export greet
func greet() int32 {
input := pdk.Input()
greeting := `Hello, ` + string(input) + `!`
pdk.OutputString(greeting)
return 0
}
func main() {}
The magic comment //export
is what exports the function to the outside world:
Compile the Plug-in
Now we must compile this plug-in to wasm. Currently the best way to do this is with tinygo:
tinygo build -o plugin.wasm -target wasi main.go
Running the Plug-In
We can now test it using the Extism CLI's run
command:
extism call plugin.wasm greet --input "Benjamin" --wasi
# => Hello, Benjamin!
At this time, all Go plug-ins need WASI to run
Documentation
Congrats! You just wrote your first Extism plug-in! To learn more about what this go library can do, see the go-pdk README.
Prerequisites
- .NET SDK 8
- WASI Workload:
dotnet workload install wasi-experimental
Install the Dependency
Create a new project and add this NuGet package to your project:
dotnet new wasiconsole -o MyPlugin
cd MyPlugin
dotnet add package Extism.Pdk
Update your MyPlugin.csproj
as follows:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
<OutputType>Exe</OutputType>
<PublishTrimmed>true</PublishTrimmed>
<!-- Make sure we create a standalone wasm file for our plugin -->
<WasmSingleFileBundle>true</WasmSingleFileBundle>
<WasmBuildNative>true</WasmBuildNative>
</PropertyGroup>
</Project>
Create an Export
The primary means of interacting with this plug-in is an export function that can be called by the outside world. Let's create a simple greeting plug-in.
using System;
using System.Runtime.InteropServices;
using System.Text.Json;
using Extism;
namespace MyPlugin;
public class Program
{
[UnmanagedCallersOnly(EntryPoint = "greet")]
public static int Greet()
{
var name = Pdk.GetInputString();
var greeting = $"Hello, {name}!";
Pdk.SetOutput(greeting);
return 0;
}
// Note: a `Main` method is required for the app to compile
public static void Main() {}
}
Compile the Plug-in
Compile as normal with dotnet:
dotnet build
Running the Plug-In
This will create a MyPlugin.wasm
file in bin/Debug/net8.0/wasi-wasm/AppBundle
. Now, you can try out your plugin by using any of the Extism SDKs or by using Extism CLI's run
command:
extism call .\bin\Debug\net8.0\wasi-wasm\AppBundle\MyPlugin.wasm greet --input "Benjamin" --wasi
# => Hello, Benjamin!
Documentation
Congrats! You just wrote your first Extism plug-in! To learn more about what this dotnet library can do, see the dotnet-pdk README and reference docs.
Prerequisites
- .NET SDK 8
- WASI Workload:
dotnet workload install wasi-experimental
Install the Dependency
Create a new project and add this NuGet package to your project:
dotnet new console -o MyPlugin -lang F#
cd MyPlugin
dotnet add package Extism.Pdk
Update your MyPlugin.fsproj
as follows:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
<OutputType>Exe</OutputType>
<PublishTrimmed>true</PublishTrimmed>
<!-- Make sure we create a standalone wasm file for our plugin -->
<WasmSingleFileBundle>true</WasmSingleFileBundle>
<WasmBuildNative>true</WasmBuildNative>
</PropertyGroup>
</Project>
Create an Export
The primary means of interacting with this plug-in is an export function that can be called by the outside world. Let's create a simple greeting plug-in.
module MyPlugin
open System
open System.Runtime.InteropServices
open Extism
[<UnmanagedCallersOnly(EntryPoint = "greet")>]
let Greet () : int32 =
let name = Pdk.GetInputString()
let greeting = $"Hello, {name}!"
Pdk.SetOutput(greeting)
0
[<EntryPoint>]
let Main args =
// Note: an `EntryPoint` function is required for the app to compile
0
Compile the Plug-in
Compile as normal with dotnet:
dotnet build
Running the Plug-In
This will create a MyPlugin.wasm
file in bin/Debug/net8.0/wasi-wasm/AppBundle
. Now, you can try out your plugin by using any of the Extism SDKs or by using Extism CLI's run
command:
extism call .\bin\Debug\net8.0\wasi-wasm\AppBundle\MyPlugin.wasm greet --input "Benjamin" --wasi
# => Hello, Benjamin!
Documentation
Congrats! You just wrote your first Extism plug-in! To learn more about what this dotnet library can do, see the dotnet-pdk README and reference docs.
Install the Dependency
The quickest way to getting started is to vendor the source for the PDK as a git submodule:
git submodule add https://github.com/extism/c-pdk extism-pdk
Create an Export
The primary means of interacting with this plug-in is an export function that can be called by the outside world. Let's create a simple greeting plug-in.
#include "extism-pdk.h"
const char *greeting = "Hello, ";
uint64_t greetingLen = 7;
int32_t greet() {
uint64_t inputLen = extism_input_length();
// Load input
uint8_t inputData[inputLen];
extism_load_input(inputData, inputLen);
// Allocate a new offset used to store greeting and name
uint64_t outputLen = greetingLen + inputLen;
ExtismPointer offs = extism_alloc(outputLen);
extism_store(offs, (const uint8_t *)greeting, greetingLen);
extism_store(offs + greetingLen, inputData, inputLen);
// Set output
extism_output_set(offs, outputLen);
return 0;
}
Compile the Plug-in
Compile with clang and target wasm32-unknown-unknown
:
clang -o plugin.wasm --target=wasm32-unknown-unknown -nostdlib -Wl,--no-entry -Wl,--export=greet main.c
Note that we must explicitly export the greet
function at compile time.
Running the Plug-In
This will create a plugin.wasm
file. Now, you can try out your plugin by using any of the Extism SDKs or by using Extism CLI's run
command:
extism call plugin.wasm greet --input "Benjamin"
# => Hello, Benjamin!
Documentation
Congrats! You just wrote your first Extism plug-in! To learn more about what this C library can do, see the c-pdk README.
Install the Dependency
This library can be used to write Extism Plug-ins in Haskell.
Docs are available on Hackage: https://hackage.haskell.org/package/extism-pdk
Install
Make sure you have wasm32-wasi-ghc installed, then generate an Executable
project with cabal:
cabal init
Add the library from Hackage to your cabal file:
build-depends: extism-pdk
We will also need to add some additional ghc options to expose the correct functions:
ghc-options:
-optl -Wl,--export=greet -optl -Wl,--export=hs_init -optl -Wl,--allow-undefined -no-hs-main -optl-mexec-model=reactor
Create an Export
The primary means of interacting with this plug-in is an export function that can be called by the outside world. Let's create a simple greeting plug-in.
module Example where
import Extism.PDK
makeGreeting g n =
output $ g ++ ", " ++ n
greet = do
-- Get a name from the Extism runtime
name <- inputString
-- Get configured greeting
greeting <- getConfig "greeting"
-- Greet the user, if no greeting is configured then "Hello" is used
makeGreeting (fromMaybe defaultGreeting greeting) name
-- Return code
return 0
foreign export ccall "greet" greet:: IO Int32
Compile the Plug-in
Compile with wasm32-wasi-cabal
:
wasm32-wasi-cabal build
cp $(find dist-newstyle/build/wasm32-wasi/ -name example.wasm) ./plugin.wasm
Note that we must explicitly export the greet
function at compile time.
Running the Plug-In
This will create a plugin.wasm
file. Now, you can try out your plugin by using any of the Extism SDKs or by using Extism CLI's run
command:
extism call plugin.wasm greet --input "Benjamin"
# => Hello, Benjamin!
Documentation
Congrats! You just wrote your first Extism plug-in! To learn more about what this Haskell library can do, see the haskell-pdk README.
Install the Dependency
Create a new Zig project:
mkdir my-plugin
cd my-plugin
zig init
Add the library as a dependency:
// build.zig.zon
.{
.name = "my-plugin",
.version = "0.0.0",
.dependencies = .{
.extism_pdk = .{
.url = "https://github.com/extism/zig-pdk/archive/<git-ref-here>.tar.gz",
// .hash = "" (zig build will tell you what to put here)
},
},
.paths = .{""},
}
Change your build.zig
so that it references extism-pdk
:
const std = @import("std");
const builtin = @import("builtin");
pub fn build(b: *std.Build) void {
comptime {
const current_zig = builtin.zig_version;
const min_zig = std.SemanticVersion.parse("0.12.0-dev.2030+2ac315c24") catch unreachable;
if (current_zig.order(min_zig) == .lt) {
@compileError(std.fmt.comptimePrint("Your Zig version v{} does not meet the minimum build requirement of v{}", .{ current_zig, min_zig }));
}
}
const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{
// if you're using WASI, change the .os_tag to .wasi
.default_target = .{ .abi = .musl, .os_tag = .freestanding, .cpu_arch = .wasm32 },
});
var basic_example = b.addExecutable(.{
.name = "basic-example",
.root_source_file = .{ .path = "examples/basic.zig" },
.target = target,
.optimize = optimize,
});
basic_example.rdynamic = true;
basic_example.entry = .disabled; // or add an empty `pub fn main() void {}` to your code
const pdk_module = b.addModule("extism-pdk", .{
.root_source_file = .{ .path = "src/main.zig" },
});
basic_example.root_module.addImport("extism-pdk", pdk_module);
b.installArtifact(basic_example);
const basic_example_step = b.step("basic_example", "Build basic_example");
basic_example_step.dependOn(b.getInstallStep());
}
Create an Export
The goal of writing an Extism plug-in is to compile your Zig code to a Wasm module with exported functions that the host application can invoke. The first thing you should understand is creating an export. Let's write a simple program that exports a greet
function which will take a name as a string and return a greeting string. Zig has excellent support for this through the export
keyword:
const std = @import("std");
const extism_pdk = @import("extism-pdk");
const Plugin = extism_pdk.Plugin;
const allocator = std.heap.wasm_allocator;
export fn greet() i32 {
const plugin = Plugin.init(allocator);
const name = plugin.getInput() catch unreachable;
defer allocator.free(name);
const output = std.fmt.allocPrint(allocator, "Hello, {s}!", .{name}) catch unreachable;
plugin.output(output);
return 0;
}
Compile the Plug-in
Note: if you started with the generated project files from
zig init
, you should deletesrc/root.zig
and any references to it if they are in yourbuild.zig
file.
Then run:
zig build
This will put your compiled wasm in zig-out/bin
.
Running the Plug-in
We can now test it using the Extism CLI's run
command:
extism call ./zig-out/bin/my-plugin.wasm greet --input "Benjamin"
# => Hello, Benjamin!
Documentation
Congrats! You just wrote your first Extism plug-in! To learn more about what this Zig library can do, see the zig-pdk README.
Install the Dependency
Create a new AssemblyScript project:
mkdir my-plugin
cd my-plugin
npm init
npm install --save-dev assemblyscript
npx asinit .
Add the library as a dependency:
npm install @extism/as-pdk@1.0.0 --save
Create an Export
The primary means of interacting with this plug-in is an export function that can be called by the outside world. Let's create a simple greeting plug-in.
import { Host, Var, Config } from '@extism/as-pdk';
function myAbort(
message: string | null,
fileName: string | null,
lineNumber: u32,
columnNumber: u32
): void { }
export function greet(): i32 {
let name = Host.inputString();
let output = "Hello, " + name + "!";
Host.outputString(output);
return 0;
}
Compile the Plug-in
Then run:
npx asc example.ts --outFile example.wasm --use abort=example/myAbort
Running the Plug-in
We can now test it using the Extism CLI's run
command:
extism call ./example.wasm greet --input "Benjamin"
# => Hello, Benjamin!
Documentation
Congrats! You just wrote your first Extism plug-in! To learn more about what this AssemblyScript library can do, see the assemblyscript-pdk README.
Need help?
If you've encountered a bug or think something is missing, please open an issue on the Extism GitHub repository.
There is an active community on Discord where the project maintainers and users can help you. Come hang out!