Introduction
Golem is a durable computing platform that makes it simple to build and deploy highly reliable distributed systems.
Golem provides transparent durability to applications by taking over execution of code. However, for performance and portability reasons, Golem does not execute machine code. Rather Golem executes code that has been compiled to WebAssembly.
WebAssembly (WASM) is a binary instruction set for a stack-based virtual machine that can be run on any platform and device. WASM is designed to be a portable, cross-language target for compilers and interpreters.
Golem has a custom executor that executes WASM code durably, by performing real-time state replication across multiple nodes. Combined with supervision and automatic recovery, this allows Golem to execute code transactionally, impervious to faults, restarts, and updates.
The WASM standard by itself does not provide any built-in I/O capabilities or system interfaces, making it insufficient for real-world applications. To address this fundamental gap, Golem adopts and fully supports the WebAssembly component model.
The component model serves as the foundation for WASI (WebAssembly System Interface), which provides crucial OS-like functionality to WASM modules. WASI defines a standardized set of system interfaces, allowing WebAssembly programs to perform essential operations such as file I/O, network access, and time management.
For Golem, this means that components can interact with the underlying system in a consistent and portable manner, regardless of the host environment.
Languages that support WASM use WASI (through the component model) to provide the functionality built into their standard libraries, which are in turn used to build libraries and frameworks that developers use to build applications.
By embracing the component model and WASI, Golem enables your WASM-based applications to benefit from the full capabilities of the underlying system, while also ensuring security and portability.
As an added benefit, the component model also facilitates cross-language interoperability, further extending Golem's flexibility as a development platform, and enabling polyglot development without having to go through bulky and slow protocols like HTTP or gRPC.
In this document, we will focus on the Golem-specific aspects of the component model, and provide high-level overview of working with components.
A full introduction to WASM, WASI, and the component model is beyond the scope of this document. For detailed information, you can refer to the official WebAssembly Component Model (opens in a new tab) and WASI (opens in a new tab) resources.
Structure
WASM components have a well-defined structure, which facilitates their execution in a host environment, as well as their interaction with other components.
The high-level structure of a WASM component is as follows:
- Code. The code section of a WASM component contains the WebAssembly bytecode that implements the component's functionality.
- Data. The data section of a WASM component contains all static data that the component requires at runtime, such as constant strings.
- Imports. The import section of a WASM component contains a list of imports that the component requires from other components or the host environment.
- Exports. The export section of a WASM component contains a list of exports that the component provides to other components or to the host environment.
In order to be executed, a WASM component must be provided with all of its dependencies, which are defined in the import section. The host environment then provides the actual implementations for these imports.
Typically, these dependencies are WASI imports, which define the low-level, OS-like functionality that the component requires to function. For example, a component that needs to read from a file might import a WASI import for file I/O.
Developers of WASM components do not typically have to know about or interact with WASI directly, because the standard library for their language of choice will be implemented in terms of WASI. In turn, frameworks and libraries that developers use are implemented in terms of the standard library, hiding details of WASI from developers.
WASM components bear some similarities to machine-code executables, but they are much richer:
A machine-code executable interacts directly with the operating system, and has only a single entry point, the main function. WASM components, on the other hand, must be passed capabilities explicitly from the host environment, and they can export many different functions, which can be called by the host environment or by other components.
Thus, conceptually, WASM components are much closer to libraries, such as shared libraries that can be used by multiple executables, rather than executables themselves.
Development
Development of WASM components requires language-specific support, including:
- A compiler that can compile the language to WASM; or, for interpreted languages, an interpreter that can be compiled to WASM.
- A standard library that is implemented in terms of WASI, and that provides the functionality that the language requires to operate, such as I/O.
- Support for importing and exporting functions, so that components developed in the language can interact with the host environment, and with other components.
You can find detailed information on building WASM components in the following language-specific guides:
There are some aspects of developing WASM components that are language- independent, and relevant to all developers working with components on Golem.
Lifecycle
The lifecycle for developing a WASM component typically involves the following steps:
- Dependencies. Identify the dependencies that the component requires, including dependencies such as WASI that are required by the language's standard library.
- Interface. Define the public interface of the component, which includes both types and functions. In serverless environments like Golem, the interface dictates how external systems may interact with the component.
- Implementation. Implement the component's functionality, including satisfying the public interface of the component, using the language's standard library and any other dependencies that are required.
- Building. Using language-specific tools, compile the implementation of the component to a WASM component; or, for interpreted languages, package the code into a WASM component, typically by bundling the interpreter.
Both steps (1) and (2) are heavily dependent on WebAssembly Interface Type (WIT), a language-independent interface description language (IDL) that is used to describe both dependencies and interface of components.
The following section provides a brief introduction to WIT.
WIT
Wasm Interface Type (WIT) is a language-independent interface description language (IDL) that is used both to describe the dependencies of a component, and to describe the interface of a component.
WIT can be compared to protocol buffers (protobuf), but there are a number of distinctions:
- Unlike protobuf, WIT is used to describe the dependencies of components;
- WIT is designed specifically for WASM components;
- WIT operates at a lower-level than protobuf, enabling cross-language interoperability—for example, a Javascript component calling into a C/C++ component.
It is beyond the scope of this document to provide a full introduction to WIT, but the following list introduces some key concepts:
- Worlds. A world contains both imports and exports, describing both required dependencies, and provided functionality.
- Types. WIT supports a number of primitive types, including integers, floats, and strings, as well as compound types such as records and lists.
- Functions. WIT supports the definition of functions, including their arguments and return types. Functions can be defined as part of an interface, and may be organized into packages.
- Resources. A resource is a special type used for representing values that exist in one component (or the host environment) and accessed elsewhere.
The following example shows a simple WIT interface that defines a function
add
that takes two integers and returns an integer:
package adder;
interface math {
add: func(a: s32, b: s32) -> s32;
}
An analogous protobuf definition for this interface would be:
syntax = "proto3";
package adder;
service Math {
rpc Add(AddRequest) returns (AddResponse);
}
message AddRequest {
int32 a = 1;
int32 b = 2;
}
message AddResponse {
int32 result = 1;
}
Further information on WIT can be found in official documentation (opens in a new tab), as well as the WIT specification (opens in a new tab).
Language-Agnostic Tooling
Although the exact toolchain used to build a WASM component will depend on the language being used, there are a number of language-agnostic tools that are useful for working with WASM components.
In this section, you will learn about these tools.
wasm-tools
The Bytecode Alliance provides a set of tools for working with WASM components,
called wasm-tools
. This command-line interface (CLI) provides a number of
related commands for working with WASM components, including:
validate
. Validate a WebAssembly file.parse
. Translate the WebAssembly text format to binary.print
. Translate the WebAssembly binary format to text.smith
. Generate a valid WebAssembly module from an input seed.mutate
. Mutate an input WASM file into a new valid WASM file.shrink
. Shrink a WASM file while preserving a predicate.dump
. Print debugging information about the binary format.objdump
. Print debugging information about section headers.strip
. Remove custom sections from a WebAssembly file.demangle
. Demangle Rust and C++ symbol names in the name section.compose
. Compose WASM components together (deprecated).component new
. Create a component from a core WASM binary.component wit
. Extract a*.wit
interface from a component.component embed
. Embed a component-type custom section in a core WASM binary.metadata show
. Show name and producer metadata in a component or module.metadata add
. Add name or producer metadata to a component or module.addr2line
. Translate WASM offsets to filename/line numbers with DWARF.completion
. Generate shell completion scripts forwasm-tools
.json-from-wast
. Convert a*.wast
file into JSON commands.
One of the most useful commands is component new
, which provides the ability
to create a WASM component from a core WASM binary. This is useful for language
ecosystems that do not yet have a native way to produce WASM components, but
which can produce WASM modules.
You can download precompiled artifacts for wasm-tools on Github (opens in a new tab). To build from source code, first install Rust (opens in a new tab), then use Cargo to build wasm-tools:
$ cargo install --force --locked wasm-tools@1.210.0
wit-bindgen
The wit-bindgen
tool, also from the Bytecode Alliance, is a tool for generating
language bindings from WIT interfaces, which makes it easier to both consume
dependencies, as well as export functionality, from components in a variety
of programming languages with immature support for the component model.
wit-bindgen
generates bindings for a number of languages, including:
- C/C++
- Rust
- Java (TeaVM)
- Go (TinyGo)
- C#
Deployment on Golem
Once you have developed a WASM component, you can deploy it on Golem. Since Golem is open source, there are an unlimited