Merancang plugin systems dengan WebAssembly: isolasi plugin di V8/SpiderMonkey, model plugin Envoy dan Extism dengan proxy-wasm, aplikasi host + wasm plugin, serta ekosistem plugin di Kubernetes dan Shopify

Setelah di episode 18 kalian memahami state management — pada episode ini kita membahas salah satu alasan terkuat mengapa perusahaan besar mengadopsi Wasm: plugin systems. Kemampuan untuk menjalankan kode pihak ketiga dengan aman di dalam aplikasi sendiri adalah kebutuhan universal — dan Wasm memberikan solusi yang tidak dimiliki JavaScript (dynamic, tidak terisolasi), Lua (kurang familiar), maupun container (terlalu berat).
Mengapa topik ini penting? Karena model "host + plugin" adalah pola arsitektur yang menggerakkan software modern: database dengan ekstensi, API gateway dengan middleware, editor dengan addon. Menguasai embedding Wasm berarti kalian bisa membangun produk yang bisa diperluas tanpa mengorbankan keamanan.
Arsitektur intinya sederhana:
Keuntungan keamanan yang dimiliki plugin Wasm (ingat episode 16):
Envoy (proxy yang dipakai Istio/Linkerd) memperkenalkan proxy-wasm — spesifikasi plugin Wasm untuk data plane. Ini contoh paling sukses plugin Wasm produksi: middleware seperti auth, rate limiting, dan observability ditulis sebagai plugin dan disuntikkan ke proxy tanpa mengganti binary.
Kontrak plugin proxy-wasm sederhana: plugin mengekspor callback, proxy memanggilnya pada peristiwa:
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
#[no_mangle]
pub fn _start() {
proxy_wasm::set_log_level(LogLevel::Info);
proxy_wasm::set_root_context(|_| Box::new(HeaderFilter));
}
struct HeaderFilter;
impl Context for HeaderFilter {}
impl HttpContext for HeaderFilter {
fn on_http_request_headers(&mut self, _num_headers: usize) -> Action {
self.add_http_request_header("x-plugin", "devvnull");
Action::Continue
}
}Deploy plugin ke Envoy via HTTP endpoint atau file — host memuat, memverifikasi, dan menjalankannya:
http_filters:
- name: envoy.filters.http.wasm
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
config:
name: "header_filter"
vm_config:
runtime: envoy.wasm.runtime.v8
code:
local:
filename: "/plugins/header_filter.wasm"Inilah model yang diadopsi banyak gateway/service mesh — dan inti dari ekosistem plugin modern.
Extism adalah framework plugin Wasm yang language-agnostic: host bisa ditulis dalam 30+ bahasa, dan plugin bisa dikompilasi dari Rust, Go, JS, Python, atau hampir semua bahasa compile-to-wasm. Ini menghilangkan hambatan "plugin harus ditulis dalam bahasa host".
Kontrak plugin didefinisikan dengan WIT-style plugin manifest:
use extism_pdk::*;
#[plugin_fn]
pub fn format_nama(nama: String) -> FnResult<String> {
Ok(format!("Plugin memproses: {nama}"))
}Host dalam JavaScript:
import { create } from "@extism/extism"
const plugin = await create(
new URL("./plugin.wasm", import.meta.url),
{ useWasi: true },
)
const out = await plugin.call("format_nama", "Budi")
console.log(out.string()) // "Plugin memproses: Budi"Karena host dan plugin punya kontrak yang sama di semua bahasa, tim backend bisa menulis plugin dalam Rust sementara plugin lain ditulis Go — keduanya berjalan di runtime yang sama.
Untuk kontrol penuh, bangun embedding sendiri — pola yang sudah kita lihat di episode 11:
use wasmtime::component::{Component, Linker};
use wasmtime::{Engine, Store};
// 1. Kontrak plugin
pub trait PluginContract {
fn proses(&mut self, input: String) -> anyhow::Result<String>;
}
// 2. Muat component plugin
let engine = Engine::default();
let component = Component::from_file(&engine, "plugin.wasm")?;
// 3. Sediakan host functions (capability minimal)
let mut linker = Linker::new(&engine);
linker.func_wrap("host", "log", |ctx, msg: String| {
println!("[plugin] {msg}");
Ok(())
})?;
// 4. Instansiasi per tenant
for tenant in ["alpha", "beta"] {
let mut store = Store::new(&engine, tenant.to_string());
let instance = linker.instantiate(&mut store, &component)?;
let proses = instance.get_typed_func::<String, String>(&mut store, "proses")?;
let hasil = proses.call(&mut store, "data dari tenant".into())?;
println!("{tenant}: {hasil}");
}Empat langkah ini — kontrak, kompilasi, host functions, instansiasi per tenant — adalah blueprint plugin systems yang dipakai di produksi.
Tip
Aturan praktis memilih pendekatan: gunakan proxy-wasm jika targetnya proxy/middleware (Envoy), Extism jika butuh plugin polyglot cepat, dan embedding custom (wasmtime) jika butuh kontrol penuh atas kontrak, lifecycle, dan capability.
Dengan SpinKube (episode 12), aplikasi Spin bisa dimuat sebagai plugin/komponen di cluster Kubernetes — plugin yang di-sidecar atau dijalankan sebagai workload ringan dengan cold start milidetik.
Shopify memakai Wasm untuk Shopify Functions — kode custom untuk diskon, shipping, dan checkout yang berjalan di infrastruktur Shopify. Merchant/tim mem-build plugin Wasm, Shopify menjalankannya di sandbox dengan capability yang dibatasi ketat — contoh produksi nyata dari "host besar menjalankan kode pihak ketiga dengan aman".
Warning
Sebelum menjalankan plugin dari pihak ketiga: jalankan cargo audit (episode 16), verifikasi tanda tangan/distribusi (kalau ada), validasi imports, dan uji di staging dengan data sintetis. Plugin yang tidak dipercaya harus diperlakukan seperti kode musuh — karena memang bisa menjadi begitu.
Pada episode 19 ini, kalian telah memahami cara merancang plugin systems dengan WebAssembly.
Inti yang harus dibawa pulang:
Di episode 20 selanjutnya kita akan membahas WASI 0.3 & roadmap 2026 — native async sebagai fondasi concurrency, fitur 0.3.x seperti cancellation dan stream optimization, serta target WASI 1.0 untuk menggantikan container di banyak kasus. Sampai jumpa di episode 20!