WASM Rust

What WASM actually gives you for business logic

Most people think of WebAssembly as a way to run C++ in the browser. Port a game engine, run a video codec, compute something expensive without dropping frames. That is a valid use case. It is not the most interesting one.

The more interesting use case is portable, sandboxed business logic. Logic that you compile once and run anywhere without touching it again. Logic that carries its permission requirements with it. Logic that behaves identically whether it is running in a browser tab, an edge node, a cloud function, or an AI orchestration pipeline.

That is what Traverse is built around. But before getting to that, it is worth being specific about what WASM actually is and what properties it actually provides.

What WASM actually is

WebAssembly is a binary instruction format. You compile source code to it and a WASM runtime executes the result. The runtime could be a browser, a Node.js process, a dedicated server runtime like Wasmtime or Wasmer, or a custom host built with a WASM embedding library.

The key property is that WASM is VM-independent. The same binary file runs in any conformant runtime. You do not recompile for each target. You do not write target-specific wrappers. The bytecode is the artifact.

This is different from JavaScript, where you ship source and the engine compiles it at runtime. It is different from Java's bytecode, which is tied to the JVM. WASM has a formal spec. Any runtime that implements the spec runs your binary correctly.

Rust is particularly well-suited for compiling to WASM. The output is compact, the runtime overhead is minimal, and Rust's ownership model means you can reason about memory behavior before the binary ships.

Why the sandbox matters for business logic

A WASM module runs in a sandbox by default. It has no access to the filesystem, no access to the network, no ability to spawn threads, no way to call host system functions. None of these things are available unless the host runtime explicitly grants them.

For most application code this sounds restrictive. For business logic it is exactly right.

Business logic should not be reading files. It should not be making outbound HTTP calls mid-calculation. It should take inputs, apply rules, and return outputs. The sandbox enforces this discipline. If a module tries to do something outside its declared scope, the runtime stops it.

The sandbox is not just a security property. It is a testability property. Logic that cannot have side effects is logic you can test in isolation, without mocking infrastructure.

When you need a capability to have access to something, you declare it. The declaration is part of the contract. The runtime reads the contract, provisions the grant if policy allows, and the capability gets exactly what it asked for. Nothing more.

What you give up

WASM is not a free lunch. There are real constraints.

No direct DOM access. A WASM module cannot reach into the page and manipulate elements. If you want UI interaction you need to pass values back to JavaScript and let JS handle the DOM. This is usually fine for business logic, which should not be touching the DOM anyway.

No native system calls without explicit grants. If your capability genuinely needs to read a config file or write to a cache, you need to declare that need in the contract and the host needs to wire up the import. It is more work than just calling fs.readFileSync. That is the point.

Startup latency on cold loads. The first time a WASM module loads there is a compilation step. Subsequent runs from cache are fast. Edge and cloud runtimes typically pre-compile and cache aggressively. For browser use, Traverse handles caching as part of placement.

WASM interface types are still maturing. Passing complex data structures between the host and the module requires care. Traverse uses a typed serialization layer defined in the contract to handle this.

What Traverse adds on top of raw WASM

Raw WASM gives you portability and sandboxing. It does not give you a way to describe what a module does, where it should run, or what it requires. You get a binary file and a set of exported function names. That is not enough to build a governed system.

Traverse adds four things on top of raw WASM.

Contracts. A structured TOML file that describes the capability's inputs, outputs, permissions, placement targets, and version. The contract is the source of truth. The runtime reads it before executing anything.

Registry. A discovery layer where capabilities are published and resolved by contract ID. Other capabilities and external callers find capabilities through the registry, not through hardcoded imports.

Placement. The runtime reads the contract's placement targets and decides where to run the capability based on current context: connectivity, latency requirements, policy constraints. The capability does not make this decision. The runtime does.

Traces. Every capability execution produces a structured trace artifact: inputs received, outputs produced, placement decision, timing. This trace is inspectable. It is the basis for audit, debugging, and AI governance.

Here is what WASM constraints look like inside a Traverse contract:

tax-calculator.capability.toml
[capability] id = "finance.tax-calculator" version = "1.3.0" [wasm] module = "./tax_calculator.wasm" memory = "8mb" [wasm.permissions] # Everything is denied by default. # Declare only what the module genuinely needs. network = false filesystem = false env_vars = false clock = true # needs current date for tax year logic host_calls = ["tax_table.lookup"] # specific host import allowed [contract] inputs = ["amount: Decimal", "jurisdiction: string", "category: TaxCategory"] outputs = ["tax_amount: Decimal", "effective_rate: f64", "breakdown: Vec<TaxLine>"] [placement] targets = ["edge", "cloud"]

The [wasm.permissions] block is explicit. Network access is denied. Filesystem access is denied. The only host call this module is allowed to make is tax_table.lookup, a specific function the host exposes for reading tax rate tables. If the module tries to do anything else, the runtime rejects it at the boundary.

Who should care

If you maintain the same logic in multiple environments, WASM with contracts is worth serious attention.

The pattern fits particularly well when the logic is:

  • Computationally non-trivial (pricing, tax, validation rules, scoring algorithms)
  • Subject to change (regulatory updates, business rule changes)
  • Required in multiple runtimes (browser, server, edge, AI pipeline)
  • Auditable (financial calculations, compliance checks, AI decisions)

If your logic is simple and lives in one place, the overhead is not worth it. Not everything needs to be a Traverse capability. But for the logic that is genuinely shared and genuinely important, compiling once and running anywhere with a machine-readable contract is a much better deal than maintaining four copies and hoping they stay in sync.

Compile once. Run anywhere.

Write your capability in Rust, define the contract, and let the Traverse runtime handle placement across browser, edge, cloud, and AI pipelines.

Get started → View on GitHub