Building WASM Components
Primatomic runs WebAssembly Components (Component Model), not plain WASM modules. Components let you define a stable interface in WIT (WebAssembly Interface Types) and generate bindings for multiple languages.
This guide covers:
- Starting from a
.witinterface (the contract) - Generating language bindings
- Compiling your implementation into a component (
.wasm) - Validating the output
Tooling Overview
Section titled “Tooling Overview”| Tool | Purpose |
|---|---|
cargo-component | Build Rust components (required for Rust) |
wasm-tools | Inspect, validate, and convert components |
wit-bindgen | Generate guest bindings (Rust) |
wit-bindgen-go | Generate Go bindings |
jco | Componentize JavaScript/TypeScript |
componentize-py | Componentize Python |
Common Workflow
Section titled “Common Workflow”1. Obtain the WIT Interface
Section titled “1. Obtain the WIT Interface”Fetch the WIT interface using wkg:
This downloads the interface files into your project’s wit/ directory:
your-project/├── wit/│ └── machine.wit└── src/ └── lib.rs2. Generate Bindings
Section titled “2. Generate Bindings”Most toolchains can generate bindings so your IDE and type checker understand what to implement. This step is optional but recommended.
3. Build a Reactor Component
Section titled “3. Build a Reactor Component”Primatomic views are reactor components (library-like, called repeatedly), not command components (executable-like).
4. Validate the Artifact
Section titled “4. Validate the Artifact”A component prints as (component ...) while a core module prints as (module ...).
wasm-tools print path/to/component.wasm | head -1wasm-tools component wit path/to/component.wasmInstall Shared Tools
Section titled “Install Shared Tools”wasm-tools
Section titled “wasm-tools”cargo install --locked wasm-toolsRust builds components using cargo-component with the wasm32-unknown-unknown target.
# Install cargo-componentcargo install cargo-component
# Add the wasm32-unknown-unknown targetrustup target add wasm32-unknown-unknown
# Create a new projectcargo new --lib my-viewcd my-viewIn Cargo.toml:
[lib]crate-type = ["cdylib"]
[dependencies]wit-bindgen = "0.41"
[profile.release]opt-level = "s"lto = trueAdd WIT and Generate Bindings
Section titled “Add WIT and Generate Bindings”Fetch the WIT interface, then generate bindings in src/lib.rs:
wit_bindgen::generate!({ path: "wit", world: "state-machine",});
struct Component;
// Implement the generated Guest traitcargo component build --release --target wasm32-unknown-unknownOutput: target/wasm32-unknown-unknown/release/<crate-name>.wasm
Validate
Section titled “Validate”# Verify it's a component (not a module)wasm-tools print target/wasm32-unknown-unknown/release/<crate-name>.wasm | head -1
# Verify no WASI importswasm-tools print target/wasm32-unknown-unknown/release/<crate-name>.wasm | grep -i wasi# (should produce no output)
# Inspect the WIT interfacewasm-tools component wit target/wasm32-unknown-unknown/release/<crate-name>.wasmGo (TinyGo)
Section titled “Go (TinyGo)”TinyGo can build Preview 2 components using wit-bindgen-go for bindings.
Fetch the WIT interface and resolve dependencies:
wkg wit buildGenerate Bindings
Section titled “Generate Bindings”Implement Exports
Section titled “Implement Exports”Set exported functions on the generated Exports struct (paths depend on your package structure).
tinygo build -target=wasip2 \ -o my-view.wasm \ --wit-world state-machine \ main.goValidate
Section titled “Validate”wasm-tools print my-view.wasm | head -1wasm-tools component wit my-view.wasm
# Verify no WASI imports (should produce no output)wasm-tools print my-view.wasm | grep -i wasiJavaScript / TypeScript
Section titled “JavaScript / TypeScript”Use jco to componentize an ES module.
Install
Section titled “Install”npm install -g @bytecodealliance/jcoEnsure your project uses ES modules ("type": "module" in package.json).
Componentize
Section titled “Componentize”jco componentize \ --wit wit/machine.wit \ --world-name state-machine \ --out my-view.wasm \ --disable=all \ view.jsValidate
Section titled “Validate”wasm-tools print my-view.wasm | head -1wasm-tools component wit my-view.wasmPython
Section titled “Python”Install
Section titled “Install”pip install componentize-pyGenerate Bindings (Optional)
Section titled “Generate Bindings (Optional)”componentize-py --wit-path wit --world state-machine bindings .Implement
Section titled “Implement”Implement the generated protocol class(es) from the bindings package.
componentize-py \ --wit-path wit/machine.wit \ --world state-machine \ componentize \ my_view \ -o my-view.wasmValidate
Section titled “Validate”wasm-tools print my-view.wasm | head -1wasm-tools component wit my-view.wasm
# Verify no WASI imports (should produce no output)wasm-tools print my-view.wasm | grep -i wasiTroubleshooting
Section titled “Troubleshooting””Not a component”
Section titled “”Not a component””You built a core module instead of a component. Verify:
wasm-tools print my-view.wasm | head -1(module= core wasm module (incorrect)(component= component (correct)
If using Rust, ensure you’re using cargo component build (not plain cargo build).
Component has WASI imports
Section titled “Component has WASI imports”If your component imports WASI capabilities, deployment will fail:
# Check for WASI importswasm-tools print my-view.wasm | grep -i wasiCommon causes:
- Rust: Built with
wasm32-wasip1orwasm32-wasip2target instead ofwasm32-unknown-unknown - Go: Built with
wasip2target - Dependencies: Some crates pull in WASI dependencies transitively
Fix by rebuilding with the correct target:
cargo component build --release --target wasm32-unknown-unknownJS Logging/Time/Randomness Not Working
Section titled “JS Logging/Time/Randomness Not Working”If you used --disable=all with jco componentize, WASI features are intentionally removed. This is expected for Primatomic views.
Deployment
Section titled “Deployment”Once validated, deploy your component:
curl -X POST https://api.primatomic.com/logs/$LOG_ID/views/my-view \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/octet-stream" \ --data-binary @my-view.wasmSee Quick Start for the complete deployment workflow.