Como usar o WASI para rodar módulos WebAssembly fora do browser
1. Fundamentos do WASI: a ponte entre WebAssembly e o sistema operacional
O WebAssembly System Interface (WASI) é uma interface padronizada que permite que módulos WebAssembly (WASM) interajam com o sistema operacional fora do ambiente do navegador. Criado pela Bytecode Alliance em 2019, o WASI resolve um problema fundamental: enquanto o WASM no browser tem acesso a APIs do navegador (DOM, WebGL, etc.), fora dele não há um padrão para acessar arquivos, rede ou variáveis de ambiente.
A principal diferença entre WASM no browser e WASI está no modelo de segurança. No browser, o sandbox é gerenciado pelo motor JavaScript. No WASI, a segurança é baseada em capacidades (capabilities): um módulo só pode acessar recursos explicitamente concedidos pelo runtime. Por exemplo, se um módulo precisa ler /etc/passwd, ele só conseguirá se o runtime permitir esse acesso na inicialização.
A arquitetura do WASI é modular. Em vez de expor chamadas de sistema tradicionais (como POSIX), ele define funções específicas para operações como abertura de arquivos, leitura de entrada padrão e obtenção de relógio do sistema. Cada função é verificada contra uma lista de permissões, garantindo que o módulo não execute operações não autorizadas.
2. Preparando o ambiente: ferramentas e runtimes WASI
O runtime mais consolidado para WASI é o Wasmtime, mantido pela Bytecode Alliance. Para instalá-lo no Linux/macOS:
curl https://wasmtime.dev/install.sh -sSf | bash
No Windows, use o instalador oficial ou winget install wasmtime.
Alternativas incluem:
- Wasmer: focado em desempenho e integração com linguagens
- WasmEdge: otimizado para edge computing e serverless
- Node.js 20+: suporte nativo a WASI via módulo wasi
Para compilar código para WASI, você precisa de um target específico. Com Rust, instale o target:
rustup target add wasm32-wasi
Para C, use o clang com --target=wasm32-wasi e a biblioteca WASI.
3. Criando seu primeiro módulo WASI: do código fonte à execução
Vamos criar um programa simples em Rust que lê entrada padrão e escreve saída. Crie um novo projeto:
cargo new --lib hello-wasi
cd hello-wasi
Edite src/lib.rs:
use std::io::{self, Write};
fn main() {
print!("Digite seu nome: ");
io::stdout().flush().unwrap();
let mut nome = String::new();
io::stdin().read_line(&mut nome).unwrap();
println!("Olá, {}!", nome.trim());
}
Adicione no Cargo.toml:
[lib]
crate-type = ["cdylib"]
[[bin]]
name = "hello-wasi"
path = "src/lib.rs"
Compile para WASI:
cargo build --target wasm32-wasi --release
O arquivo gerado estará em target/wasm32-wasi/release/hello-wasi.wasm. Execute com Wasmtime:
echo "Maria" | wasmtime hello-wasi.wasm
Saída esperada:
Digite seu nome: Olá, Maria!
4. Acesso a recursos do sistema com WASI: arquivos, diretórios e rede
Para manipular arquivos, o módulo WASI precisa de permissões explícitas. Exemplo em Rust que lê um arquivo:
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let file = File::open("/dados/entrada.txt").unwrap();
let reader = BufReader::new(file);
for linha in reader.lines() {
println!("{}", linha.unwrap());
}
}
Compile e execute concedendo acesso ao diretório:
cargo build --target wasm32-wasi --release
wasmtime --dir /caminho/real/dados::/dados leitor.wasm
O mapeamento --dir /caminho/real/dados::/dados faz com que o diretório real seja visto como /dados pelo módulo.
Para rede, o WASI Preview 1 não tem suporte nativo. É necessário usar extensões como wasi-experimental-http ou aguardar o Preview 2.
5. Integração com linguagens e ecossistemas existentes
Python com wasmtime-py
Instale a biblioteca:
pip install wasmtime
Execute o módulo WASI:
import wasmtime
store = wasmtime.Store()
module = wasmtime.Module(store.engine, open("hello-wasi.wasm", "rb").read())
linker = wasmtime.Linker(store)
linker.define_wasi(store)
wasi_config = wasmtime.WasiConfig()
wasi_config.stdin = b"João\n"
store.set_wasi(wasi_config)
linker.module(store, "", module)
func = linker.get_default(store, "")
func(store)
Node.js com suporte nativo a WASI
import { WASI } from 'wasi';
import { readFileSync } from 'fs';
const wasi = new WASI({
args: [],
env: {},
preopens: { '/dados': './dados' }
});
const wasm = readFileSync('./hello-wasi.wasm');
const { instance } = await WebAssembly.instantiate(wasm, {
wasi_snapshot_preview1: wasi.wasiImport
});
wasi.start(instance);
Exemplo prático: calculadora CLI
Crie calculadora.rs:
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 4 {
eprintln!("Uso: calculadora <a> <op> <b>");
return;
}
let a: f64 = args[1].parse().unwrap();
let b: f64 = args[3].parse().unwrap();
let resultado = match args[2].as_str() {
"+" => a + b,
"-" => a - b,
"*" => a * b,
"/" => a / b,
_ => { eprintln!("Operador inválido"); return; }
};
println!("{}", resultado);
}
Compile e execute:
cargo build --target wasm32-wasi --release
wasmtime calculadora.wasm 10 + 5
Saída: 15
6. Segurança e sandboxing: o modelo de capacidades do WASI
O modelo de capacidades do WASI é mais restritivo que contêineres Docker. Enquanto um contêiner compartilha o kernel do host, o WASI executa em um sandbox isolado sem acesso direto a chamadas de sistema.
No Wasmtime, configure permissões granulares:
# Apenas um diretório específico
wasmtime --dir ./dados_permitidos modulo.wasm
# Variáveis de ambiente controladas
wasmtime --env DATABASE_URL=localhost modulo.wasm
# Mapeamento de diretórios
wasmtime --mapdir /host/path::/sandbox/path modulo.wasm
Vantagens sobre Docker: inicialização em milissegundos, menor footprint de memória, sem necessidade de container runtime. Desvantagens: não substitui Docker para aplicações que precisam de rede completa ou múltiplos processos.
7. Casos de uso reais e limitações do WASI em 2025
Aplicações serverless: Cloudflare Workers e Fastly Compute@Edge usam WASI para executar código seguro em edge nodes. O modelo de capacidades permite que cada função tenha acesso mínimo aos recursos.
Plugins seguros: SQLite adotou WASI para executar extensões de banco de dados com segurança. TensorFlow Lite também permite modelos compilados para WASI.
Limitações atuais:
- Sem suporte a threads (embora proposto para Preview 3)
- Rede limitada (apenas HTTP via extensões experimentais)
- Sistema de arquivos incompleto (sem symlinks, permissões Unix)
- Performance inferior a nativo em operações intensivas de CPU
8. Futuro do WASI: o que esperar das próximas gerações (WASI 0.2+)
O WASI Preview 2 (já disponível em runtimes como Wasmtime 14+) traz APIs importantes:
- HTTP cliente e servidor: requisições e respostas HTTP
- Criptografia: chaves, hashes e assinaturas
- Cron: agendamento de tarefas temporizadas
- Observabilidade: métricas e tracing
O WASI Preview 3, em desenvolvimento, introduz um modelo baseado em componentes (Component Model), permitindo composição de módulos WASI com tipagem forte e interfaces versionadas.
Para migrar módulos Preview 1 para Preview 2, use a ferramenta wasm-tools:
cargo install wasm-tools
wasm-tools component new modulo-preview1.wasm -o modulo-preview2.wasm
O ecossistema avança rapidamente. Em 2025, espera-se que WASI seja o padrão para computação portátil e segura, rivalizando com contêineres em cenários específicos.
Referências
- WASI Overview - Bytecode Alliance — Documentação oficial sobre a arquitetura e objetivos do WASI
- Wasmtime Installation Guide — Guia oficial de instalação do runtime Wasmtime
- Rust and WebAssembly - Compiling to WASI — Tutorial oficial da Rust Foundation sobre compilação para WASI
- WASI Preview 2 Specification — Especificação técnica das APIs do WASI Preview 2
- Node.js WASI Documentation — Documentação oficial do módulo WASI no Node.js
- Wasmer: WebAssembly Runtime — Site oficial do runtime Wasmer com exemplos de uso
- Cloudflare Workers and WebAssembly — Como o Cloudflare Workers utiliza WASI para serverless
- WASI: The WebAssembly System Interface - Mozilla Hacks — Artigo técnico da Mozilla sobre a padronização do WASI