Build a plugin
The initial SDK targets Rust and WASI 0.2 components. Install the component target:
rustup target add wasm32-wasip2
The reference implementation uses Rust 1.98. Plugin source can use compatible Rust releases, but its generated component must satisfy the supported WIT API. Native subprocess, OS-specific and unrestricted networking crates are not automatically available inside a component.
Start with the SDK
The SDK lives at plugin_system/sdk in the
Memona repository. Until a crates.io
release is published, use a checked-out, pinned Memona revision and a path
dependency:
[package]
name = "my-memona-plugin"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
memona-plugin-sdk = { path = "../memona/plugin_system/sdk" }
The path is relative to your own Cargo.toml; adjust it to your checkout. Pin
the checkout revision so your build does not silently pick up an API change.
The SDK includes the canonical WIT and generated Rust types. Implement its
Guest trait and invoke its export! macro for your implementation type. You
do not need wasm-bindgen, wasm-pack, or WIT copies in your plugin repository.
cargo doc also exposes the generated Rust API.
struct MyProvider;
// Implement every method of memona_plugin_sdk::Guest for MyProvider.
// Unsupported optional operations return ErrorCode::Unsupported.
memona_plugin_sdk::export!(MyProvider);
This short excerpt shows registration, not a complete implementation. The full
buildable example is plugin_system/test-component/src/lib.rs. It is a test
fixture, not a WebDAV or IPFS client. From a Memona checkout, build it with:
cargo build --manifest-path plugin_system/Cargo.toml \
-p memona-plugin-test-component --target wasm32-wasip2 --release
Build your own plugin with:
cargo build --release --target wasm32-wasip2
Call the host
Use the SDK’s generated host interface for network requests. For example, this helper retrieves a bounded HTTP response through the connection’s permissions:
use memona_plugin_sdk::memona::filesystem::host;
pub fn get_bytes(url: &str) -> Result<Vec<u8>, host::HostError> {
let response = host::http(&host::Request {
method: "GET".into(),
url: url.into(),
headers: Vec::new(),
body: Vec::new(),
})?;
if !(200..300).contains(&response.status) {
return Err(host::HostError::Unavailable);
}
Ok(response.body)
}
Map HTTP statuses to appropriate filesystem errors in a real provider. Supply authentication through request headers using connection configuration; do not hard-code tokens. The host resolves and pins addresses on every request and on every redirect it follows.
Nothing crosses whole. A request body is pushed in pieces of at most 1 MiB and
a response body is pulled the same way, so a file of any size moves without
either side holding it. The same shape governs reader and writer, which is
how a provider serves a file larger than one call allows.
A protocol that is not HTTP uses connect for a TCP stream or bind for a
datagram socket, each on a package-declared port. The host resolves and pins
DNS and can start TLS so certificate checking stays outside the guest.
Prepare a release
Copy the resulting .wasm component to plugin.wasm, write
manifest.json, and ZIP the files at the archive root. Name the
archive with a .memona-plugin suffix. A directory wrapping the files inside
the ZIP is not accepted.
Use the filesystem behavior guide to handle revisions, unsupported operations and mutation failures correctly. Before publishing, exercise your provider against its real service and test lost connections.