WASM Runtime

calimero-runtime

50+
host functions
Wasmer
engine
Cranelift
compiler
16
VM limits

Purpose

Wasmer-based WASM execution engine. The Engine compiles application modules using Cranelift. Module::run builds a VMLogic instance with shared and private Storage, an optional NodeClient, and a ContextHost. It calls the exported method, catches panics, and returns an Outcome containing the return value, logs, events, and storage delta.

crates/runtime

Execution Flow

From SDK-generated WASM to executed outcome in a single synchronous call path.

SDK Rust / TS / JS .wasm compiled module Engine::compile Cranelift JIT → Module Module::run(method, VMContext, Storage) Storage (shared) Storage (private) NodeClient? ContextHost Wasmer Store + Imports instantiate + call execute exported fn, catch panics VMLogic.finish() collect results + delta Outcome return_value, logs, events, delta → caller

VMContext & VMLimits

The VMContext carries all input data and identity information needed for a single execution. VMLimits enforces resource boundaries to prevent runaway modules.

VMContext

pub struct VMContext {
    input: Vec<u8>, // serialized args
    context_id: ContextId,
    executor_public_key: PublicKey,
    governance_epoch: u64,
    // group DAG heads for consistency
}

VMLimits

pub struct VMLimits {
    max_memory_pages: u32,
    max_stack_size: u32,
    max_registers: u64,
    max_register_size: u64,
    max_logs: u64,
    max_log_size: u64,
    max_events: u64,
    max_event_size: u64,
    max_xcalls: u64,
    max_storage_key_size: u64,
    max_storage_value_size: u64,
    max_method_name_len: u32,
    max_module_size: u64,
}

Limits are enforced at the host function boundary — each call checks remaining budget before proceeding. Exceeding any limit causes the method to abort with a LimitExceeded error, and all mutations are rolled back.

Host Functions

The runtime imports 50+ host functions into the WASM module. Each function is registered as a Wasmer import under the "env" namespace. Organized by domain:

Identity & I/O
context_id
Write the current context ID into a register
executor_id
Write the executor's public key into a register
input
Read the method's serialized input arguments into a register
value_return
Set the return value for the method call
log_utf8
Emit a UTF-8 log message (counted against max_logs)
panic
Abort execution with an error code
panic_utf8
Abort execution with a UTF-8 error message
Registers
register_len
Get the byte length of data in a register
read_register
Copy register contents into WASM linear memory

Registers act as a transfer buffer between host and guest. Host functions write results into numbered registers; the guest reads them out. This avoids complex multi-return calling conventions.

Events & Cross-Context
emit
Emit a typed event (broadcast to subscribed clients)
emit_with_handler
Emit an event with a named handler method for reactive processing
xcall
Cross-context call — invoke a method on another context (async, enqueued)

Events are accumulated in the Outcome and broadcast to WebSocket/SSE subscribers after execution completes. Cross-context calls are queued and executed asynchronously.

KV Storage (shared)
storage_read
Read a value by key from shared context state
storage_write
Write a key-value pair to shared context state
storage_remove
Delete a key from shared context state
storage_has
Check if a key exists in shared context state
commit
Flush pending mutations to a changeset
persist_root_state
Compute and persist the Merkle root hash of current state
read_root_state
Read the last computed root state hash
apply_storage_delta
Apply a remote delta to local storage (used during sync)
flush_delta
Flush accumulated storage changes as a CausalDelta
Private Storage
private_storage_read
Read from node-local private state (never synced to peers)
private_storage_write
Write to node-local private state
private_storage_remove
Delete from node-local private state

Private storage is scoped to the local node. Use cases include caching, local preferences, and derived data that doesn't need to be replicated.

CRDT / JS Bridge Collections

These host functions back the SDK's CRDT collection types. The js_std_d_* prefix indicates they implement the JavaScript SDK's distributed data structures.

Map operations

js_std_d_map_new
Create a new distributed map
js_std_d_map_get / set / delete
Read, write, and remove entries
js_std_d_map_entries / keys / values
Iterate over map contents

Set operations

js_std_d_set_new
Create a new distributed set
js_std_d_set_add / delete / has
Mutate and query set membership

Vector & RGA operations

js_std_d_vector_new / push / get
Distributed vector (append-mostly)
js_std_d_rga_*
Replicated Growable Array for ordered sequences

User & Frozen storage

user_storage_*
Higher-level user-facing storage API
frozen_storage_*
Read-only snapshot of storage at a specific point
Blobs
blob_bytes
Read blob content by ID (returns data into register)
blob_exists
Check if a blob exists in the local store
blob_create
Store new blob content (content-addressed, returns BlobId)
blob_available
Check if a blob is locally available (may need to fetch from peers)
Merge Registration
__calimero_register_merge
Register a WASM-exported merge function for custom CRDT conflict resolution

Applications can export a custom merge function. When the runtime applies a remote delta and detects a conflict, it invokes the registered merge function instead of using the default strategy. This enables application-specific conflict resolution beyond LWW.

Storage Interface

The runtime uses a simple KV Storage trait that abstracts over the actual storage backend. This allows test isolation via InMemoryStorage.

pub trait Storage {
    fn get(&self, key: &[u8]) -> Option<Vec<u8>>;
    fn set(&mut self, key: Vec<u8>, value: Vec<u8>);
    fn remove(&mut self, key: &[u8]) -> Option<Vec<u8>>;
    fn has(&self, key: &[u8]) -> bool;
}

InMemoryStorage

A HashMap<Vec<u8>, Vec<u8>> implementation used in unit tests and the SDK's local test harness. Provides identical semantics to the RocksDB-backed storage without any external dependencies.

RocksDB-backed Storage

In production, the Storage trait is implemented by a thin wrapper over the calimero-store Database, scoped to the executing context's State / PrivateState columns.