When using WebAssembly (Wasm) on platforms different than the browser, we have to care about distribution. How can we securely transfer our Wasm modules to desired environments? How can we verify integrity to ensure Wasm modules were not compromised during distribution?

Let’s take a look at the container world. Container registries (those that implement the Docker Registry 2.0 standard (or to be more precise, the OCI distribution spec)) are a trusted distribution channel that addresses all the questions raised before. This does not mean that we have to package our Wasm modules as containers! Container registries can store and distribute more than just containers. Container registries can store any OCI compliant artifact. And that’s exactly what we will create from our Wasm modules to leverage container registries as a trusted distribution channel.

Create a Wasm module in Rust

For demonstration purposes, we will create a small Wasm module that calculates the product of three numbers. Nothing fancy here. Just something that we can take, package as OCI artifact, and distribute using a container registry.No worries, you don’t need to set up Rust - if you don’t want to - to follow this article. The Rust application and the resulting Wasm module are available on GitHub in this repository.

First, let’s create a new Rust library project using cargo new sample --lib.

We have to tell the Rust compiler that we want a dynamic system library (which results in a .wasm file when compiling to the wasm32-wasi platform). We do so by updating the Cargo.toml file and specifying cdylib as crate-type:

[package]
name = "sample"
version = "0.1.0"
edition = "2018"

[dependencies]

[lib]
crate-type = ['cdylib']

Now, we will replace the contents of lib.rs with the following:

/// returns the product calculated from three parameters v1, v2, and v3
 #[no_mangle]
pub extern "C" fn multiply(v1: i32, v2: i32, v3: i32) -> i32 {
    v1 * v2 *v3
}

To create a Wasm module from our Rust code, we have to compile it, targeting the wasm32-wasi platform:

# compile library crate for the wasm32-wasi platform
cargo build --target wasm32-wasi 

At this point, we can also test the Wasm module locally using wasmtime to verify it works as expected:

# Execute the multiply function via wasmtime
wasmtime --invoke multiply ./target/wasm32-wasi/debug/sample.wasm 3 3 3
27

Great! Our module works as expected, so we can finally distribute our module as OCI compliant artifact.

Publish OCI artifact in Azure Container Registry

We need an OCI distribution spec-compliant registry as the distribution channel. I will use an instance of Azure Container Registry (ACR). Let’s quickly create a new ACR instance for the sake of demonstration:

# variables
rgName="rg-blog-sample"
location="germanywestcentral"
acrName="blogsample"
subId="YOUR_AZURE_SUBSCRIPTION_ID"

# Login to Azure
az login

# Select Subscription
az account set --subscription $subId

# Create a Resource Group
az group create -n $rgName -l $location

# Create a Container Registry
az acr create -n $acrName\
  -g $rgName\
  -l $location\
  --sku Basic\
  --admin-enabled true

We can use a small tool called wasm-to-oci to distribute Wasm modules using a container registry.

Install wasm-to-oci

You can download wasm-to-oci - pre-compiled for your operating system - directly from its releases page on GitHub. On macOS and Linux, ensure that x file-mode is set (chmod +x wasm-to-oci). Add wasm-to-oci to your PATH environment variable and reload the terminal session for all operating systems.

Authenticate against ACR using Docker CLI

wasm-to-oci uses authentication established via Docker CLI under the hood. That said, let’s quickly authenticate against ACR. The following snippet will use the administrative credentials for ACR (az acr credential show -n blogsample). If you’ve access to an ACR with Premium SKU, you should create an individual token to and configure fine-granular access control for authentication and authorization instead.

# use Docker CLI to authenticate against ACR
docker login blogsample.azurecr.io -u blogsample -p yOurP@55w0rD

Push Wasm modules to ACR

Finally! We can push our Wasm module using wasm-to-oci push and provide a fully-qualified name for the OCI artifact. The actual qualifier (here blogsample.azurecr.io) may differ if you’re using something different than ACR:

# push wasm module as OCI artifact to ACR
wasm-to-oci push sample.wasm blogsample.azurecr.io/sample-wasm:0.0.1

Pull Wasm modules from ACR

We can use wasm-to-oci also on the other side to pull Wasm modules. On the consumer side, you also have to authenticate against ACR using Docker CLI. Once authenticated, wasm-to-oci pull is used to pull the desired Wasm module.

When wasm-to-oci finished downloading the OCI artifact, the .wasm file will be placed in the current folder.

# pull OCI artifact
wasm-to-oci pull blogsample.azurecr.io/sample-wasm:0.0.1

# verify WASM module works by invoking multiply again
wasmtime --invoke multiply sample.wasm 4 4 4
64

Conclusion

Using container registries such as Azure Container Registry as a distribution channel for Wasm modules helps a lot. Container registries are trusted services with enterprise degree authentication and authorization capabilities baked in. wasm-to-oci simplifies the distribution process dramatically and addresses both needs distribution and consumption.

Several other container registries support OCI compliant artifacts too. You are not limited to Azure Container Registry at this point. However, integration with other Azure services and flexible authentication and authorization mechanisms (especially when using ACR on premium SKU) are great arguments why you should use ACR here.