WASM, WASI, WAGI,WHAT?

WebAssemly

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

 

WebAssembly is neither Web nor Assembly. It is (memory) safe, fast, efficient, and sandboxed. Something we want from container technologies!

WebAssemly

The two main principles to uphold in Wasm:

 

1. Portability

2. Security

WebAssemly - Portability (POSIX way)

POSIX provides source code portability. You can compile the same source code with different versions of libc to target different machines. Still, this is not good enough, we need portable binaries.

WebAssemly - Portability (Wasm way)

WebAssemly - Security

Wasm is sandboxed:

  • The code can't "talk" directly to the OS.
  • The host needs to provide functions to access the OS capabilities, which are not standardized across different OSs.
  • The capabilities available to the sandbox can be limited, and exceptions are made explicit.

 

Just as WebAssembly is an assembly language for a conceptual machine, it needs a system interface for a conceptual OS - the WebAssembly System Interface (WASI)!

WebAssemly - Where are now?

Rust, at 26%, is the most frequently used language, followed by C++ then AssemblyScript. (survey responses are available on demand as a CSV file)

Chart from https://blog.scottlogic.com

WebAssemly - Where are now?

AssemblyScript is a new TypeScript-like language that has been designed specifically for WebAssembly.

Chart from https://blog.scottlogic.com

WebAssemly - Where are now?

Chart from https://blog.scottlogic.com

WebAssemly - Where are now?

Chart from https://blog.scottlogic.com

WebAssembly is expected to have a significant impact across quite a wide range of different application areas, and 56% seeing Serverless as a high impact application.

WebAssemly - Where are now?

Chart from https://blog.scottlogic.com

WASI

WASI is the WebAssembly System Interface, a modular system interface for Wasm - files, network connections, clocks, and random numbers (controlled by host for sandboxing).

WASI

All WASI is Wasm,

but not all Wasm is WASI.

WASI

There are two things that distinguish WASI modules from “ordinary” Wasm:

 

  1. WASI modules may import one or many pre-defined WASI interfaces.
  2. Although the Wasm start instruction is valid, WASI modules export by convention a function as _start to be called by the host runtime.

 

For example, when a WASI module calls __wasi_fd_write() there needs to be a host to pass that imported function so it is able to actually do something.

WASI - hosts

  • Hosts are the WebAssembly Runtimes that run WebAssembly modules -- e.g. V8 (browser), Wasmtime, Lucet, Wasmer, Wasm3.
  • Most WebAssembly runtimes / interpreters can be used as a command line interface, or embedded/linked in a larger application by using it's library API.
  • Each of these projects has their own strengths, and it really depends on what our use case is to choose the best host for us. And there are many more projects out there to choose from.

WASI - guests

  • Guests are the WebAssembly modules that are executed by the host -- the application that is run inside the host.
  • The host is able to provide additional functionality to guest, by doing tasks on the guests' behalf. This functionality is offered by passing functions to the importObject (see example below).
  • Uses a capability based security model. For example in Wasmtime, by default, the guest module cannot access any part of the host's filesystem. The user that invokes Wasmtime must pass in the --mapdir or --dir flag to grant modules the capability to access directories in the host filesystem.

WASI - guests (Go example)

const go = new Go(); // Defined in wasm_exec.js. Don't forget to add this in your index.html.

const runWasm = async () => {
  // Get the importObject from the go instance.
  const importObject = go.importObject;

  // Set our function we want to use from WebAssembly, on our importObject
  // This should be set on the env key, of the import object.
  // The function itself should be set on the env object, where the key is
  // depending on the compiler, usually in the format similar to below,
  // but you can check for the "LinkError: WebAssembly.instantiate()",
  // which would show the name required.
  importObject.env["./main.go.jsPrintInt"] = value => console.log(value);

  // Instantiate our wasm module
  const wasmModule = await wasmBrowserInstantiate("./main.wasm", importObject);

  // Allow the wasm_exec go instance, bootstrap and execute our wasm module
  go.run(wasmModule.instance);

  // Call the export from wasm
  wasmModule.instance.exports.printIntFromWasm(24);
};
runWasm();

WASI - things to consider

  • very limited network API (pending proposals, 1, 2).
  • we need to check if the current crypto API supports our requirements (algorithms, symmetric and asymmetric operations).
  • lacking parallel execution (proposal exists)
  • lack of default interface type sytem (WAIT)

WebAssembly Gateway Interface

WAGI allows developers to run WebAssembly WASI binaries as HTTP handlers. It's basically the old idea of CGI HTTP handlers applied to WASI, as a way of building a server implementation on top of WASI.

 

This is very useful to be able to go around some key architectural limitations in WASI, particularly its lack of a networking layer and its underlying single-threaded nature.

 

It is NOT considered production-grade by its developers, neither is it "supported" software.

WebAssembly Interface Types (WAIT)

Problem: Wasm lacks support for complex types (only numbers are supported). if a function takes or returns anything besides numbers, things get complicated.

WAIT

The problem to solve here is translating values between different types when a module is talking to another module (or directly to a host, like the browser).

 

There are four places where a translation is needed.

 

For exported functions:

  • accepting parameters from the caller        
  • returning values to the caller

For imported functions:

  • passing parameters to the function
  • accepting return values from the function

WAIT

With interface types, the two sides aren’t trying to share a representation. Instead, the default is to copy values or pointers between one side and the other.

WAIT - Why not use Protobuffers?

Protobuffer is designed so that you can interact with a system that you don’t share memory with -- either because it’s running in a different process or because it’s on a totally different machine across the network.

 

This means that you have to be able to send the thing in the middle -- the "intermediate representation" of the objects -- across that boundary.

WAIT - Why not use Protobuffers? (cont.)

With interface types, this "IR" never needs to leave the engine. The modules only see the what the engine spits out for them at the end of the process -- what's been copied to their linear memory or given to them as a reference. There is no schema/layout that needs to be specified.

WAIT - Status

Not production ready. The official proposal is on Github.

 

Implemented in:

  • the Rust toolchain
  • wasm-bindgen
  • the Wasmtime WebAssembly runtime

WASM, WASI, WHAT?

By Andrei

WASM, WASI, WHAT?

  • 1,156