calimero-node 0.10.0

Core Calimero infrastructure and tools
# Node Integration Guide

How to integrate Calimero Node into your application.

---

## Overview

The node layer provides **ready-to-use** synchronization and event handling. You typically don't integrate it directly—instead, you use it via `merod` (the node daemon) or embed it in your server.

---

## Using merod (Recommended)

### Start Node Daemon

```bash
# Start node with default config
merod --config config.toml

# Or with environment variables
CALIMERO_NODE_PORT=2428 \
CALIMERO_SYNC_INTERVAL=5 \
merod
```

### Configuration File

```toml
# config.toml
[node]
port = 2428
host = "0.0.0.0"

[sync]
interval = 5
frequency = 10
timeout = 30
max_concurrent = 30

[storage]
path = "./data"
```

### Interact via HTTP API

```bash
# Create context
curl -X POST http://localhost:2428/contexts \
  -H "Content-Type: application/json" \
  -d '{"protocol": "near"}'

# Execute transaction
curl -X POST http://localhost:2428/execute/:context_id \
  -H "Content-Type: application/json" \
  -d '{
    "method": "add_item",
    "args": {"name": "Widget"}
  }'

# Query state
curl http://localhost:2428/query/:context_id?method=get_items
```

---

## Embedding in Server

### Setup

```toml
[dependencies]
calimero-node = { path = "../node" }
calimero-context = { path = "../context" }
tokio = { version = "1.0", features = ["full"] }
actix = "0.13"
```

### Start Node Manager

```rust
use calimero_node::{start, NodeConfig};
use calimero_context::ContextClient;
use calimero_node_primitives::NodeClient;

#[tokio::main]
async fn main() -> Result<()> {
    // Create clients
    let context_client = ContextClient::new(/* ... */);
    let node_client = NodeClient::new(/* ... */);
    
    // Create config
    let config = NodeConfig {
        sync_config: Default::default(),
        ..Default::default()
    };
    
    // Start node
    let node_handle = start(
        blobstore,
        sync_manager,
        context_client,
        node_client,
    ).await?;
    
    // Node running in background
    println!("Node started");
    
    // Keep running
    tokio::signal::ctrl_c().await?;
    
    Ok(())
}
```

---

## Creating Contexts

### Via Node Client

```rust
use calimero_primitives::context::ContextId;

// Create context
let context_id = node_client.create_context(protocol).await?;

// Subscribe to context (for sync)
node_client.subscribe(&context_id).await?;

// Node will automatically:
// - Create DeltaStore for context
// - Subscribe to gossipsub topic
// - Start periodic sync
```

---

## Executing Transactions

### Via Context Client

```rust
// Execute method
let outcome = context_client.execute(
    &context_id,
    &executor_id,
    "add_item",
    borsh::to_vec(&args)?,
    vec![],  // attachments
    None,    // no parent
).await?;

// Node automatically:
// - Runs WASM
// - Creates CausalDelta
// - Broadcasts via gossipsub
// - Updates DAG heads
```

---

## Handling Events

### Define Event Handler

```rust
#[app::event]
pub enum MyEvent {
    ItemAdded { name: String },
}

#[app::event_handler]
impl MyApp {
    pub fn on_item_added(&mut self, event: MyEvent) {
        // Handler runs on receiving nodes
        self.counter.increment();
    }
}
```

### Node Automatically

- Receives delta via gossipsub
- Checks if event has handler
- Skips if author node
- Executes handler in WASM
- Emits to WebSocket clients

---

## Monitoring

### Health Check

```rust
use actix_web::{web, App, HttpServer};

async fn health() -> Result<String> {
    let stats = node.get_stats().await?;
    Ok(format!("Alive: {} contexts", stats.context_count))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/health", web::get().to(health))
    })
    .bind("0.0.0.0:8080")?
    .run()
    .await
}
```

### Metrics

```rust
// Export Prometheus metrics
use prometheus::{Encoder, TextEncoder};

async fn metrics() -> Result<String> {
    let encoder = TextEncoder::new();
    let metric_families = prometheus::gather();
    
    let mut buffer = vec![];
    encoder.encode(&metric_families, &mut buffer)?;
    
    Ok(String::from_utf8(buffer)?)
}
```

---

## Testing

### Integration Test

```rust
#[tokio::test]
async fn test_node_integration() {
    // Setup
    let node = start_test_node().await;
    let context_id = create_test_context(&node).await;
    
    // Execute transaction
    let result = node.execute(
        &context_id,
        "add_item",
        &args,
    ).await.unwrap();
    
    assert!(result.success);
    
    // Wait for propagation
    tokio::time::sleep(Duration::from_millis(200)).await;
    
    // Verify on other node
    let other_node = start_test_node().await;
    other_node.subscribe(&context_id).await.unwrap();
    
    // Sync should happen automatically
    tokio::time::sleep(Duration::from_secs(1)).await;
    
    // Verify state
    let state = other_node.query(&context_id, "get_items").await.unwrap();
    assert_eq!(state.items.len(), 1);
}
```

---

## Best Practices

### 1. Use Presets

```rust
// Don't configure everything manually
let config = NodeConfig::production();  // Use preset

// Only override what you need
let config = NodeConfig {
    sync_config: SyncConfig {
        interval: Duration::from_secs(2),
        ..Default::default()
    },
    ..NodeConfig::production()
};
```

### 2. Monitor Pending Deltas

```rust
// Check periodically
tokio::spawn(async move {
    let mut interval = tokio::time::interval(Duration::from_secs(60));
    loop {
        interval.tick().await;
        
        for (context_id, delta_store) in node.delta_stores.iter() {
            let stats = delta_store.pending_stats().await;
            if stats.count > 100 {
                warn!("Context {} has {} pending deltas", context_id, stats.count);
            }
        }
    }
});
```

### 3. Handle Errors Gracefully

```rust
match node.execute(&context_id, method, args).await {
    Ok(outcome) => {
        // Success
    }
    Err(e) => {
        error!("Execution failed: {}", e);
        // Don't panic - log and continue
    }
}
```

### 4. Clean Up Resources

```rust
// On context delete
node.unsubscribe(&context_id).await?;
node.delete_delta_store(&context_id).await?;

// Periodic GC
tokio::spawn(async move {
    let mut interval = tokio::time::interval(Duration::from_secs(3600));
    loop {
        interval.tick().await;
        node.gc().await?;
    }
});
```

---

## See Also

- [Architecture]architecture.md - How node works
- [Sync Configuration]sync-configuration.md - How to configure
- [Event Handling]event-handling.md - Event system
- [Performance]performance.md - Performance tuning