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