Back to top

Wascap Specification - Communications

In WebAssembly, communication between a guest module and the host is what most developers would call primitive. WebAssembly 1.0 only supports 32- and 64-bit integers and floating point numbers. So any high-level communication performed between the guest and the host must somehow, underneath it all, use nothing but these primitive parameter types.

Some tools designed specifically for integration with JavaScript and the browser will auto-generate strongly typed methods for each call, as well as generate wrapper classes to allow JavaScript to instantiate (and hold onto the reference for) structs. This requires a complex juggling act where both sides of the communication exchange must maintain and expose their own memory management systems. In the case of the early Wascap specification–which was inspired by wasm-bindgen–this memory management system bled out into the interface contract.

Wasm-bindgen allows for both sides to create instances of things that will persist between function calls. This makes sense if you are trying to allow a seamless language integration with JavaScript, but this is not Wascap’s goal. The wascap standard is about the facilitation of function calls, and it does not presume to support any kind of arbitrary object allocation across the guest<–>host boundary.

WebAssembly Interface Specification

WebAssembly Interfaces are a text format for clearly defining the set of imports and exports that define a particular interface. The text format itself is inspired by the lisp-like WebAssembly text format (wat). For example, the WASI interface contains a fairly lengthy set of imports, and manually checking whether or not a given module has the right set of them would be impractical and error-prone.

Thankfully, the folks at Wasmer have added support for WebAssembly Interfaces to wapm.io and the accompanying CLI tooling. With this tooling you can quickly check to see if a given Wasm file conforms to the WASI interface or the Wascap interface (or any interface that may potentially be recognized in the future).

The following is the formal definition of the Wascap interface (v 0.0.2) as used by Wasmer:

(interface "wascap"
    ;; Mandatory wascap imports
    (func (import "wascap" "__host_call" )          (param i32 i32))    
    (func (import "wascap" "__guest_request")       (param i32))
    (func (import "wascap" "__guest_request_len")   (result i32))
    (func (import "wascap" "__guest_response")      (param i32 i32))    
    (func (import "wascap" "__guest_error")         (param i32 i32))
    (func (import "wascap" "__host_response")       (param i32))
    (func (import "wascap" "__host_response_len")   (result i32))
    (func (import "wascap" "__console_log")         (param i32 i32))

    ;; Mandatory wascap guest module exports    
    (func (export "__guest_call"))    
)

Note - there is a useful tutorial on Publishing to the Wapm Registry that relies on wapm.io’s ability to support multiple interfaces and illustrates how this interface specification can be used.

Required Guest Functions

This function is mandatory and must be implemented by any Wascap-compliant guest module.
FunctionParametersDescription
__guest_callThis function is called by the host to execute a function contained within the Wasm module. It takes no parameters, as the queries for input parameters and sets for output parameters are done by calling the appropriate host functions.

Required Host Functions

These functions are mandatory and must be implemented by any Wascap-compliant host.
They must be made available to the guest in the import namespace wascap. Note that while type signatures may indicate a pointer to const u8 or i32, but all of those types are converted into i32 before marshalling between host and guest. The preceding specification shows what the compiled Wasm module must import and export.
FunctionParametersDescription
__host_callptr - *const u8
len - usize
Called by the guest to invoke a host function.
The parameters indicate the pointer (start) and length of an arbitrary blob of bytes for the request.
__guest_request_lenreturns usizeAsks the host to for the size in bytes of the request it wants processed. This function is called by the guest prior to calling __guest_request.
__guest_requestptr - *const u8Asks the host to populate the guest request by sending it a pointer where the data should be placed.
__guest_responseptr - *const u8
len - usize
Sets the value of the response the host will use once the __guest_call function returns. While this function can be called as many times as you like per invocation, you should try and only call it once for performance reasons.
__guest_errorptr - *const u8
len - usize
Sets a string value of an error that occurred while the guest module was in the middle of a __guest_call. If this is set, the host will assume the function call failed and use the string in log output.
__host_response_lenreturns usizeThe guest module will call this function to determine the size of the host response after __host_call terminates. If the host failed to process the request, this value will be 0.
__host_responseptr - *const u8After __host_call terminates, the guest module will invoke this function to allow the host to populate the response (an arbitrary blob of bytes) starting at the given pointer.
__console_logptr - *const u8
len - usize
While executing code within __guest_call, the guest module can log arbitrary strings (UTF-8 encoding) to whatever STDOUT device is attached to the host by calling this function.

Wascap Host Runtime (Waxosuit) Compatibility

While the core wascap interface considers the payloads that are communicated between guest and host to be completely opaque, if you want your wascap-compliant Wasm guest module to be able to use Waxosuit’s secure, cloud-native capabilities, the binary payloads must be in the form of Commands and Events, as implemented in the wascap codec. These payloads are simple envelopes that use the protobuf Any type to wrap opaque payloads. In other words, you’ll need to use the Command and Event to carry your domain-specific payload, but your payloads can be anything you choose so long as both the capability provider and guest module understand them.