Applications
Calimero applications are WASM (WebAssembly) modules that run inside the Calimero node runtime. They use the Calimero SDK to manage CRDT-backed state, emit events, and interact with the network.
Application Model
Section titled “Application Model”Applications are compiled from Rust or TypeScript to WebAssembly and executed in a sandboxed environment. They use the Calimero SDK to:
- Define state using CRDT collections
- Implement logic that mutates state
- Emit events for real-time updates
- Access private storage for node-local data
Quick Start
Section titled “Quick Start”See core/crates/sdk/README.md for complete SDK documentation.
Minimal example:
use calimero_sdk::app;use calimero_sdk::borsh::{BorshDeserialize, BorshSerialize};use calimero_storage::collections::{LwwRegister, UnorderedMap};
#[app::state]#[derive(Debug, BorshSerialize, BorshDeserialize)]#[borsh(crate = "calimero_sdk::borsh")]pub struct MyApp { items: UnorderedMap<String, LwwRegister<String>>,}
#[app::logic]impl MyApp { #[app::init] pub fn init() -> MyApp { MyApp { items: UnorderedMap::new(), } }
pub fn add_item(&mut self, key: String, value: String) -> app::Result<()> { self.items.insert(key, value.into())?;
Ok(()) }
pub fn get_item(&self, key: &str) -> app::Result<Option<String>> { Ok(self.items.get(key)?.map(|v| v.get().clone())) }}Build and deploy with meroctl. See SDK Guide for details.
Architecture
Section titled “Architecture”WASM Execution Flow
Section titled “WASM Execution Flow”flowchart LR
CLIENT[Client] --> NODE[Node]
NODE --> WASM[WASM]
WASM --> STORAGE[Storage]
STORAGE --> NODE
NODE --> NETWORK[Network]
NODE --> CLIENT
style CLIENT fill:#1a1a1a,stroke:#00ff00,stroke-width:3px,color:#ffffff
style NODE fill:#000000,stroke:#00ff00,stroke-width:4px,color:#ffffff
style WASM fill:#1a1a1a,stroke:#00ff00,stroke-width:3px,color:#ffffff
style STORAGE fill:#000000,stroke:#00ff00,stroke-width:4px,color:#ffffff
style NETWORK fill:#1a1a1a,stroke:#00ff00,stroke-width:3px,color:#ffffff
Execution Model
Section titled “Execution Model”- Deterministic execution: WASM code runs deterministically for consistent results across nodes
- Sandboxing: Isolated execution environment with resource limits
- State mutation: CRDT operations generate actions that are collected in DELTA_CONTEXT
- Event emission: Applications can emit events for real-time updates
CRDT State Management
Section titled “CRDT State Management”Applications use CRDT collections for conflict-free state. See core/crates/storage/README.md for complete CRDT documentation.
Available collections:
Counter- Distributed counters (sum)LwwRegister<T>- Last-write-wins registersUnorderedMap<K,V>- Key-value mapsVector<T>- Ordered listsUnorderedSet<T>- Unique sets (union)Option<T>- Optional CRDTs
CRDTs can be nested arbitrarily for complex data structures.
Event System
Section titled “Event System”Applications emit events for real-time updates. Events propagate to all peers automatically.
Event lifecycle:
- Emitted during method execution
- Broadcast to all peers via delta
- Handlers execute on peer nodes
- Handlers can update state or trigger side effects
See core/crates/sdk/README.md for event examples.
Private Storage
Section titled “Private Storage”For node-local data (secrets, caches) that never syncs across nodes:
...#[derive(BorshSerialize, BorshDeserialize, Debug)]#[borsh(crate = "calimero_sdk::borsh")]#[app::private]pub struct Secrets { secrets: UnorderedMap<String, String>,}
...pub fn add_secret(&mut self, id: String, secret: String) -> app::Result<()> { let mut secrets = Secrets::private_load_or_default()?; let mut secrets_mut = secrets.as_mut(); secrets_mut .secrets .insert(id.clone(), secret.clone())?; Ok(()) }...See core/crates/sdk/README.md for private storage details.
Views vs Mutations
Section titled “Views vs Mutations”In Rust, methods using &self are read-only views (no deltas generated), while &mut self methods are mutations:
// View method (read-only, no delta generated)pub fn get_item(&self, key: &str) -> app::Result<Option<String>> { self.items.get(key)?.map(|v| v.get().clone())}
// Mutation method (generates delta)pub fn set_item(&mut self, key: String, value: String) -> app::Result<()> { self.items.insert(key, value.into())?; Ok(())}Benefits:
- View methods (
&self) don’t generate storage deltas - Faster execution (no persistence overhead)
- Clear intent in API (Rust’s type system enforces immutability)
Resource Limits
Section titled “Resource Limits”WASM execution is bounded:
- Memory: Configurable limits (default: ~64MB)
- Stack size: Bounded to prevent stack overflow
- Execution time: Metered with gas-like system
- Register limits: Number and size of storage registers
See core/crates/runtime/README.md for detailed limits.
ABI Generation
Section titled “ABI Generation”Applications export an ABI (Application Binary Interface) that clients use:
- Build WASM: Compile Rust or TypeScript code to WASM
- Generate ABI: Extract method signatures, types, events
- Client bindings: Generate TypeScript/Python clients from ABI
- Type safety: Full type information for client calls
Tools:
calimero-abi: Rust tool for ABI generation@calimero-network/abi-codegen: TypeScript client generator
Example Applications
Section titled “Example Applications”- kv-store: Simple key-value store (
core/apps/kv-store) - blobs: File/blob sharing (
core/apps/blobs) - battleships: Multiplayer game (
battleships)
Deep Dives
Section titled “Deep Dives”For detailed application development:
- SDK Documentation:
core/crates/sdk/README.md- API reference and examples - Storage Collections:
core/crates/storage/README.md- CRDT types and merge semantics - Runtime:
core/crates/runtime/README.md- WASM execution engine - Integration Guide:
core/crates/node/readme/integration-guide.md- Building applications
Related Topics
Section titled “Related Topics”- Getting Started - Complete getting started guide
- Contexts - Application instances
- Architecture Overview - How applications fit into the system