Extism WebAssembly bridge plugin for bub.
- Bub plugin entry point:
extism - One Bub hook adapter per configured Extism plug-in
- Standard Extism manifest support
bub extismmanagement commands:listshowaddremove
- Python-side proxies for hook surfaces that need Bub runtime objects:
run_model_streamprovide_channelsprovide_tape_storeregister_cli_commands
bub-extism does not replace Bub's pluggy model. It loads as a normal Bub
plugin, then registers one hook adapter for each configured wasm plug-in.
uv pip install "git+https://github.com/bubbuild/bub-contrib.git#subdirectory=packages/bub-extism"You can also install it with Bub:
bub install bub-extism@main- Python 3.12+
- The
extismPython runtime package is installed with this package - WASI-enabled modules must set
"wasi": truein Bub config
For example builds:
- Rust example:
cargorustupwasm32-unknown-unknowntarget
- Go example:
- Go with
GOOS=wasip1 GOARCH=wasmsupport
- Go with
By default, bub-extism reads ~/.bub/extism.json.
Use BUB_EXTISM_CONFIG_PATH=/path/to/extism.json to override the config path.
Example:
{
"plugins": {
"prompt": {
"manifest": {
"wasm": [
{
"path": "/absolute/path/to/prompt.wasm"
}
]
},
"hooks": {
"build_prompt": "build_prompt"
}
},
"model": {
"manifest": {
"wasm": [
{
"path": "/absolute/path/to/model.wasm"
}
],
"allowed_hosts": ["api.example.com"],
"config": {
"provider": "demo"
}
},
"wasi": true,
"hooks": {
"run_model": "run_model"
}
}
}
}Configuration rules:
- Each entry under
pluginsis one Bub hook adapter backed by one Extism plug-in. manifestis a standard Extism manifest object.wasistays on the Bub side because WASI enablement is a host/runtime decision.hooksmaps Bub hook names to exported wasm functions.
- Bub still owns hook dispatch and precedence.
bub-extismregisters one Python adapter per configured entry.- You can split hooks across multiple plug-ins or keep them in one module.
Typical layouts:
- one plug-in for
build_prompt - one plug-in for
run_model - one combined plug-in exporting both
resolve_sessionbuild_promptrun_modelrun_model_streamload_statesave_staterender_outbounddispatch_outboundregister_cli_commandsonboard_configon_errorsystem_promptprovide_tape_storeprovide_channelsbuild_tape_context
bub-extism adds a management group similar to bub mcp:
bub extism list
bub extism show prompt
bub extism add prompt ./prompt.manifest.json --hook build_prompt=build_prompt
bub extism remove promptbub extism add expects:
- one standard Extism manifest JSON file
- one or more
--hook HOOK=EXPORTbindings
If a wasm plug-in exposes register_cli_commands, its commands are registered
into the same bub extism group.
Each exported hook function receives one UTF-8 JSON object.
run_model request:
{
"abi_version": "bub.extism.v1",
"hook": "run_model",
"args": {
"prompt": "hello",
"session_id": "cli:local",
"state": {}
}
}build_prompt request:
{
"abi_version": "bub.extism.v1",
"hook": "build_prompt",
"args": {
"message": {
"content": "hello"
},
"session_id": "cli:local",
"state": {}
}
}Bridge behavior:
- Bub runtime internals such as
_runtime_*fields are removed fromstate - Non-JSON-serializable values are skipped before the wasm call
Valid return shapes:
Plain text:
hello from wasm
Wrapped value:
{
"value": "hello from wasm"
}Skip current hook:
{
"skip": true
}Return an error:
{
"error": {
"message": "missing api key"
}
}provide_channels returns channel descriptors:
{
"value": [
{
"name": "wasm",
"pollIntervalSeconds": 1,
"functions": {
"start": "channel_start",
"poll": "channel_poll",
"send": "channel_send",
"stop": "channel_stop"
}
}
]
}provide_tape_store returns a tape store descriptor:
{
"value": {
"functions": {
"list_tapes": "tape_list_tapes",
"fetch_all": "tape_fetch_all",
"append": "tape_append",
"reset": "tape_reset"
}
}
}register_cli_commands returns command descriptors:
{
"value": [
{
"name": "hello",
"help": "Run the hello command.",
"function": "cli_hello"
}
]
}That command is exposed as:
bub extism hello '{"name":"Bub"}'See examples/README.md for three verified paths:
- Rust
run_modelon its own - Go
build_prompton its own - Go
build_promptplus Rustrun_modeltogether
From the repository root:
uv run --python 3.12 --no-project \
--with-editable ./bub \
--with-editable ./bub-contrib/packages/bub-extism \
--with pytest \
--with pytest-asyncio \
-m pytest bub-contrib/packages/bub-extism/tests -qTo verify example builds and composition only:
uv run --python 3.12 --no-project \
--with-editable ./bub \
--with-editable ./bub-contrib/packages/bub-extism \
--with pytest \
--with pytest-asyncio \
-m pytest bub-contrib/packages/bub-extism/tests/test_examples.py -q