WASI (WebAssembly System Interface) adalah daftar fungsi standar yang boleh di-import modul Wasm untuk bicara dengan sistem: membaca argumen, menulis ke layar terminal, membuka file, membaca jam, mengambil angka acak. Dengan WASI, .wasm yang sama bisa jalan di server dan di terminal, bukan cuma di browser. Bayangin Bu Sri menyewa tukang dari agen untuk mengolah foto kamar. Tukangnya bekerja di satu ruangan, dan semua permintaan harus lewat formulir standar ke Bu Sri. Bu Sri cuma memberi kunci lemari foto. Lemari rahasia berisi data tamu gak pernah dikasih kuncinya, jadi tukangnya bahkan gak tahu lemari itu ada. Tukangnya modul Wasm, formulirnya fungsi WASI, Bu Sri adalah runtime (host), dan kunci lemari itu folder yang di-preopen.
🧪 Tebakan cepat: program foto Bu Sri dijalankan dengan izin ke folder
fotosaja (--dir foto), lalu diminta membacafoto/../rahasia/tamu.csv. Path-nya diawalifoto/. Berhasil atau ditolak? Tebak dulu, jawabannya ada di bagian "Capability: izin per folder".
Program foto yang sama, sekarang di terminal
Ini foto-abu, program Rust biasa yang memakai std::fs. Gak ada wasm-bindgen, gak ada JavaScript:
//! foto-abu: jadikan foto RGBA abu-abu. Jalan di server lewat WASI.
use std::{env, fs, process};
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
eprintln!("pakai: foto-abu <masuk.rgba> <keluar.rgba>");
process::exit(2);
}
let mut px = match fs::read(&args[1]) {
Ok(isi) => isi,
Err(e) => {
eprintln!("gagal membaca {}: {e}", args[1]);
process::exit(1);
}
};
for p in px.chunks_exact_mut(4) {
let abu = ((p[0] as u32 * 77 + p[1] as u32 * 150 + p[2] as u32 * 29) >> 8) as u8;
p[0] = abu;
p[1] = abu;
p[2] = abu;
}
if let Err(e) = fs::write(&args[2], &px) {
eprintln!("gagal menulis {}: {e}", args[2]);
process::exit(1);
}
println!("{}: {} piksel jadi abu-abu", args[2], px.len() / 4);
}
rustup target add wasm32-wasip1
cargo build --release --target wasm32-wasip1 # target/wasm32-wasip1/release/foto-abu.wasm, 63.172 byte
- Di browser, import modul disediakan JavaScript. Di sini import-nya 13 fungsi dari
wasi_snapshot_preview1, antara lainargs_get,fd_read,fd_write,path_open,fd_prestat_get, danproc_exit. Runtime-lah yang menyediakannya. kamar-teras.rgbaadalah foto 1200 × 800 dalam byte RGBA mentah, 3.840.000 byte.- Target ini dulu bernama
wasm32-wasi. Rust 1.78 menambah namawasm32-wasip1, dan nama lama dihapus di Rust 1.84.
Menjalankan dengan Wasmtime
Wasmtime adalah runtime WASI dari Bytecode Alliance. Ini keluaran aslinya:
$ wasmtime --version
wasmtime 49.0.2 (3c8a3e79a 2026-10-02)
$ wasmtime foto-abu.wasm foto/kamar-teras.rgba foto/kamar-teras-abu.rgba
gagal membaca foto/kamar-teras.rgba: No such file or directory (os error 44)
$ wasmtime --dir foto foto-abu.wasm foto/kamar-teras.rgba foto/kamar-teras-abu.rgba
foto/kamar-teras-abu.rgba: 960000 piksel jadi abu-abu
$ wasmtime --dir foto::/data foto-abu.wasm /data/kamar-teras.rgba /data/kamar-teras-abu.rgba
/data/kamar-teras-abu.rgba: 960000 piksel jadi abu-abu
wasmtime file.wasm argumen...menjalankan_start, yaitumain()dari Rust. Argumen sesudah nama file diteruskan ke program.- Tanpa
--dir, programnya gak punya satu folder pun. Folderfotoada di disk, tapi bagi modul, file itu "No such file or directory".os error 44adalah nomor error WASI untuknoent. --dir fotomembuka folderfotodi host dan memberikannya ke modul dengan nama yang sama. Ini disebut preopen.--dir foto::/datamemberikan folder yang sama dengan nama lain. Modul melihat/data, bukan path asli di disk.- Hasilnya sama byte demi byte dengan grayscale JavaScript di pelajaran sebelumnya.
Capability: izin per folder
WASI memakai model capability: modul cuma bisa memakai apa yang diberikan host secara eksplisit. Bawaannya: gak ada file, gak ada jaringan, gak ada environment variable. Yang ada cuma argumen, stdin, stdout, stderr, jam, dan angka acak.
$ wasmtime --dir foto foto-abu.wasm rahasia/tamu.csv foto/curi.rgba
gagal membaca rahasia/tamu.csv: No such file or directory (os error 44)
$ wasmtime --dir foto foto-abu.wasm foto/../rahasia/tamu.csv foto/curi.rgba
gagal membaca foto/../rahasia/tamu.csv: Operation not permitted (os error 63)
Jadi jawaban tebakannya: ditolak. foto/../rahasia/tamu.csv dibuka lewat folder foto, dan .. gak boleh keluar dari folder yang diizinkan. Wasmtime menolaknya dengan os error 63 (perm). Bahkan kalau --dir rahasia juga diberikan, path yang lewat foto/.. tetap ditolak; yang boleh cuma rahasia/tamu.csv langsung. Bandingkan dengan program native: dia bisa membaca semua file yang bisa dibaca user yang menjalankannya.
Node: node:wasi
Node punya modul node:wasi untuk menjalankan modul WASI preview 1:
// jalankan.mjs
import { readFile } from 'node:fs/promises';
import { WASI } from 'node:wasi';
const wasi = new WASI({
version: 'preview1', // wajib: 'preview1' (atau 'unstable')
args: ['foto-abu', 'foto/kamar-teras.rgba', 'foto/kamar-teras-abu.rgba'],
preopens: { foto: './foto' }, // nama di dalam modul: folder di host
});
const modul = await WebAssembly.compile(await readFile('foto-abu.wasm'));
const instance = await WebAssembly.instantiate(modul, wasi.getImportObject());
process.exitCode = wasi.start(instance); // _start = main() dari Rust; hasilnya kode keluar
$ node jalankan.mjs
(node:91328) ExperimentalWarning: WASI is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
foto/kamar-teras-abu.rgba: 960000 piksel jadi abu-abu
preopenssama dengan--dir: kiri nama di dalam modul, kanan folder di host.wasi.start()mengembalikan kode keluar (returnOnExitbawaannyatrue), jadi oper keprocess.exitCode.- Path
foto/../rahasia/tamu.csvjuga ditolak, tapi pesannya beda:Capabilities insufficient (os error 76)(notcapable). - Statusnya masih Stability: 1 - Experimental di dokumentasi Node 22, 24, dan 26. Dokumentasinya juga menulis bahwa
node:wasibelum memberi keamanan filesystem selengkap runtime WASI lain: jangan dipakai untuk menjalankan kode yang gak dipercaya.
Preview 1, WASI 0.2, WASI 0.3
- Preview 1 (
wasi_snapshot_preview1, juga disebut WASI 0.1): daftar fungsi gaya C, datanya lewat pointer dan panjang di linear memory. Dukungannya paling luas: Wasmtime, Node, runtime edge, juga Go (GOOS=wasip1) dan TinyGo (-target=wasip1). - WASI 0.2 (preview 2, resmi 25 Januari 2024): dibangun di atas Component Model. Interface-nya ditulis di WIT (
wasi:cli,wasi:filesystem,wasi:sockets,wasi:clocks,wasi:random,wasi:io,wasi:http), dan hasilnya sebuah komponen, bukan modul biasa. Rust:--target wasm32-wasip2(tier 2 sejak Rust 1.82).foto-abuversi ini 89.702 byte, dan Wasmtime menjalankannya dengan perintah yang sama. - WASI 0.3 (rilis 11 Juni 2026, patch 0.3.1 Agustus 2026): async langsung di Component Model (
stream,future,async func), danwasi:iodihapus. wasi.dev menyebutnya rilis terbaru; WASI 0.2 tetap didukung luas. Di Rust 1.99,wasm32-wasip3masih tier 3.
node:wasi cuma mengerti preview 1. Komponen ditolak sejak byte ke-4:
$ node jalankan-comp.mjs # jalankan.mjs, tapi membaca foto-abu-component.wasm
(node:91330) ExperimentalWarning: WASI is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
node:internal/modules/run_main:123
triggerUncaughtException(
^
[CompileError: WebAssembly.compile(): expected version 01 00 00 00, found 0d 00 01 00 @+4]
Komponen dibahas lengkap di pelajaran "Wasm Component Model".
📌 Ukuran dan waktu mulai
Diukur di MacBook M1 Pro yang sedang sibuk, waktu satu proses penuh (mulai, baca foto, olah, tulis), median 11 putaran yang dijalankan bergantian:
| Cara | Ukuran | Waktu |
|---|---|---|
| Program native Rust (arm64) | 302.656 byte | 5,9 ms |
| Wasmtime, hasil compile di-cache (bawaannya) | 63.172 byte | 15,1 ms |
Wasmtime, -C cache=n (compile tiap kali) |
63.172 byte | 27,4 ms |
Wasmtime, .cwasm dari wasmtime compile |
215.584 byte | 16,1 ms |
| Wasmtime, komponen WASI 0.2 | 89.702 byte | 19,4 ms |
Node 22, node jalankan.mjs |
63.172 byte | 40,3 ms (Node kosong 29,4 ms) |
TinyGo 0.43 (-target=wasip1) |
302.365 byte | |
Go 1.27.2 (GOOS=wasip1) |
2.677.059 byte |
- Sebagian besar waktu itu ongkos memulai proses. Di dalam runtime yang sudah jalan, membuat instance baru dari modul yang sudah di-compile jauh lebih murah: di Node 22, 0,4 sampai 0,6 ms untuk modul WASI di pelajaran ini, alias ratusan mikrodetik.
- Karena itu platform serverless berbasis Wasm membuat satu instance baru per request: mulainya cepat, dan tiap request terpisah satu sama lain.
- Dibanding container, modul Wasm gak membawa sistem file Linux: satu file 63 KB, dan izinnya ditolak semua sampai diberikan.
⚠️ Jebakan yang sering ketemu
- Lupa
--dir. Folder-nya ada di disk, tapi modul melihatNo such file or directory (os error 44). - Mengira
foto/..boleh keluar. Path dibuka relatif ke folder yang diizinkan, dan keluar dari situ ditolak (os error 63di Wasmtime,76di Node). - Memakai
node:wasiuntuk kode orang lain. Masih experimental, dan dokumentasinya sendiri bilang jangan untuk kode yang gak dipercaya. Pakai Wasmtime. - Menjalankan komponen WASI 0.2 di runtime preview 1.
expected version 01 00 00 00, found 0d 00 01 00. Pilih target yang cocok dengan runtime-nya. - Lupa meneruskan kode keluar.
wasi.start()mengembalikan angkanya; tanpaprocess.exitCode, Node selesai dengan 0 walau programnya gagal.
TL;DR: WASI adalah antarmuka standar antara modul Wasm dan sistem: argumen, stdout, file, jam, angka acak. Program Rust biasa di-build dengan --target wasm32-wasip1 (63.172 byte, 13 import wasi_snapshot_preview1) dan dijalankan dengan wasmtime (49.0.2) atau node:wasi (masih Experimental, version: 'preview1' wajib). Model keamanannya capability: tanpa --dir foto (atau preopens) modul gak melihat file apa pun (os error 44), dan foto/../rahasia tetap ditolak (os error 63 di Wasmtime, 76 di Node). Preview 1 dukungannya paling luas; WASI 0.2 (Januari 2024) dan 0.3 (Juni 2026) memakai Component Model, hasilnya komponen yang dijalankan Wasmtime tapi ditolak node:wasi. Satu proses Wasmtime butuh sekitar 15 ms dengan cache atau .cwasm (27 ms kalau compile tiap kali, native 6 ms), sedangkan instance baru di runtime yang sudah jalan butuh kurang dari 1 ms.