This implementation of a calculator in Reverse Polish Notation (RPN) serves to demonstrate the functionnality of the Wasm component model.
Checkout the long_running
branch to build an executable that takes a long time to run.
There are two essential scripts to run the demo:
./setup.sh
which will build all the binaries, sign them and publish them if you choose to do so (publishing requires a GitHub token)./run.sh
which will verify the signatures of the components, compose them and run the resulting binary withwasmtime
(and calling the audit process--audit
)
The scripts require following tools:
cargo-vet
for generating vet information of the source codecargo-auditable
for building the auditable binarieswasmadder-cli
for adding vet info to the wasm binarieswasmsign2-cli
for signing the wasm binarieswac-cli
for composing the wasm binarieswkg
for publishing the wasm binariescosign
for signing and verifying published packages- my custom
wasmtime
fork: wasmtime for running packages
The setup.sh
script automates the building and signing process of the project's components using both safe and vulnerable versions of dependencies. It performs the following steps:
- Generate Signing Keys: Creates public and secret keys for signing the WebAssembly components.
- Build and Sign with Safe Dependencies: Builds and signs the components using the original
Cargo.toml
with vetted dependencies. - Switch to Unvetted Supply Chain: Replaces the supply chain folders to use unvetted dependencies and rebuilds and signs the components.
- Switch to Vulnerable Dependencies: Replaces the
Cargo.toml
withVulnerableCargo.toml
to build and sign the components with vulnerable dependencies. - Restore Original State: Restores the original
Cargo.toml
and supply chain folders to their safe versions. - Optional Publishing: Offers the option to publish the safe composed binary to GitHub Container Registry (
ghcr.io
), signing it with Cosign for verification.
The run.sh
script automates the verification and execution of the composed binary using the following steps:
- Verify the package's signature: Verifies the signatures of the components using
cosign
. - Download the package: Downloads the package from the GitHub Container Registry (
ghcr.io
). - Runs the package: Runs the package using
wasmtime
and the--audit
flag to perform the audit process.
The example is divided into two seperate components:
- The
rpn
library responsible for implementing the calculator logic - The
command
binary which uses the library to do some math (similar to themain
function of a normal program)
To be able to execute the example in a WebAssembly runtime such as wasmtime
, we first need to generate the component bindings, build the component binaries and then compose them.
The generated bindings.rs
files must be kept up-to-date with the wit
specifications in wit/*/world.wit
and will be updated with the cargo-component build command:
cargo component build --target=<TARGET>
Where TARGET
is something wasmy from here that supports the component model, I recommend wasm32-wasip2
but that requires rustc nightly.
FYI: this command must be run in each subfolder that contains component source code i.e. ./command/
and ./rpn
.
The components that are generated (in command or rpn
+ /target/<TARGET>/debug/
must now be composed using wac
:
wac plug command/target/<TARGET>/debug/command.wasm --plug rpn/target/<TARGET>/debug/rpn.wasm -o composed.wasm
This step is crucial as it plugs
the library into the sockets of the command program. This means that the exports of the library are bound to the imports of the command
component. The resulting composed.wasm
binary does not export rpn
's exports but only those specifed by command
.
E.g. wasmtime
$ wasmtime run composed.wasm
3
The subsequent output should be the result of the math that we did in the command
component using the rpn
library.