Skip to main content

Prerequisites

Before you begin, ensure you have:
  • Rust 1.67 or later installed (Install Rust)
  • Basic familiarity with Rust and async programming
  • A text editor or IDE configured for Rust development

Installation

Add TAPLE Core to your project’s Cargo.toml:
Cargo.toml
[dependencies]
taple-core = "0.4.0"
tokio = { version = "1.20", features = ["full"] }
hex = "0.4.3"
The examples in this guide use tokio for async runtime and hex for encoding. Make sure to include these dependencies.

Basic Usage Example

This complete example demonstrates the core workflow of TAPLE Core:
1

Import TAPLE Core

Start by importing the necessary modules:
use std::str::FromStr;
use taple_core::crypto::*;
use taple_core::request::*;
use taple_core::signature::*;
use taple_core::*;
2

Generate Node Keys

Create a cryptographic key pair for your node identity:
#[tokio::main]
async fn main() {
    // Generate random node ID key pair
    let node_key_pair = crypto::KeyPair::Ed25519(
        Ed25519KeyPair::from_seed(&[])
    );
}
Using from_seed(&[]) generates a random key pair. In production, you should manage keys securely and persistently.
3

Configure Node Settings

Set up your node configuration:
// Configure minimal settings
let settings = {
    let mut settings = Settings::default();
    settings.node.secret_key = hex::encode(
        node_key_pair.secret_key_bytes()
    );
    settings
};
The Settings struct allows you to configure:
  • Node identity and cryptographic keys
  • Network settings (listen addresses, bootstrap nodes)
  • Database backend
  • Protocol parameters
4

Build the Node

Initialize the TAPLE node with your settings:
// Build node with in-memory database (for testing only)
let (mut node, api) = Node::build(
    settings, 
    MemoryManager::new()
).expect("TAPLE node built");
This returns:
  • node: The node instance for receiving notifications
  • api: The API interface for interacting with the network
MemoryManager is only for testing. For production, implement a persistent database using the DatabaseManager trait.
5

Create a Governance Subject

Every TAPLE network needs at least one governance subject:
// Create a minimal governance
// First, add a key for the governance
let governance_key = api
    .add_keys(KeyDerivator::Ed25519)
    .await
    .expect("Error getting server response");

// Compose the subject creation request
let create_subject_request = EventRequest::Create(StartRequest {
    governance_id: DigestIdentifier::default(),
    name: "".to_string(),
    namespace: "".to_string(),
    schema_id: "governance".to_string(),
    public_key: governance_key,
});
6

Sign and Submit the Request

Sign the request with your node’s private key:
// Create signed request
let signed_request = Signed::<EventRequest> {
    content: create_subject_request.clone(),
    signature: Signature::new(
        &create_subject_request, 
        &node_key_pair
    ).unwrap(),
};

// Send the signed request to the node
let _request_id = api
    .external_request(signed_request)
    .await
    .unwrap();
7

Receive Event Notifications

Wait for the event to be processed:
// Wait until event notification
let subject_id = if let Notification::NewEvent { sn: _, subject_id } = 
    node.recv_notification()
        .await
        .expect("NewEvent notification received") 
{
    subject_id
} else {
    panic!("Unexpected notification");
};

println!("Governance created with ID: {}", subject_id);
8

Query Subject Data

Retrieve the created subject:
// Get the new subject data
let subject = api
    .get_subject(DigestIdentifier::from_str(&subject_id).unwrap())
    .await
    .unwrap_or_else(|_| panic!("Error getting subject"));

println!("Subject ID: {}", subject.subject_id.to_str());
println!("Subject SN: {}", subject.sn);
9

Graceful Shutdown

Always shut down the node gracefully:
// Shutdown the node
node.shutdown_gracefully().await;

Complete Example

Here’s the complete working example from the TAPLE Core repository:
examples/basic_usage/src/main.rs
use std::str::FromStr;

use taple_core::crypto::*;
use taple_core::request::*;
use taple_core::signature::*;
use taple_core::*;

/**
 * Basic usage of TAPLE Core. It includes:
 * - Node initialization with on-memory DB (only for testing purpose)
 * - Minimal governance creation example
 */
#[tokio::main]
async fn main() {
    // Generate random node ID key pair
    let node_key_pair = crypto::KeyPair::Ed25519(
        Ed25519KeyPair::from_seed(&[])
    );

    // Configure minimal settings
    let settings = {
        let mut settings = Settings::default();
        settings.node.secret_key = hex::encode(
            node_key_pair.secret_key_bytes()
        );
        settings
    };

    // Build node
    let (mut node, api) = Node::build(
        settings, 
        MemoryManager::new()
    ).expect("TAPLE node built");

    // Create a minimal governance
    // Compose and sign the subject creation request
    let governance_key = api
        .add_keys(KeyDerivator::Ed25519)
        .await
        .expect("Error getting server response");
        
    let create_subject_request = EventRequest::Create(StartRequest {
        governance_id: DigestIdentifier::default(),
        name: "".to_string(),
        namespace: "".to_string(),
        schema_id: "governance".to_string(),
        public_key: governance_key,
    });
    
    let signed_request = Signed::<EventRequest> {
        content: create_subject_request.clone(),
        signature: Signature::new(
            &create_subject_request, 
            &node_key_pair
        ).unwrap(),
    };

    // Send the signed request to the node
    let _request_id = api.external_request(signed_request).await.unwrap();

    // Wait until event notification
    let subject_id = if let Notification::NewEvent { sn: _, subject_id } = 
        node.recv_notification()
            .await
            .expect("NewEvent notification received") 
    {
        subject_id
    } else {
        panic!("Unexpected notification");
    };

    // Get the new subject data
    let subject = api
        .get_subject(DigestIdentifier::from_str(&subject_id).unwrap())
        .await
        .unwrap_or_else(|_| panic!("Error getting subject"));

    println!("Subject ID: {}", subject.subject_id.to_str());

    node.shutdown_gracefully().await;
}

Running the Example

To run the basic usage example:
Terminal
# Clone the repository
git clone https://github.com/opencanarias/taple-core.git
cd taple-core

# Run the example
cargo run --example basic_usage
Expected output:
Subject ID: <generated-subject-identifier>

Understanding the Workflow

The basic TAPLE workflow consists of:
  1. Node Initialization: Create a node with settings and database backend
  2. Request Creation: Compose event requests (Create, Fact, Transfer, EOL)
  3. Request Signing: Sign requests with cryptographic keys
  4. Request Submission: Send signed requests via the API
  5. Event Processing: Node processes and validates the request
  6. Notification: Receive notifications about processed events
  7. Query State: Retrieve subject data and event history
This example creates a governance subject, which is the foundation of any TAPLE network. Governance defines the rules and permissions for the network.

Next Steps

Now that you have a basic TAPLE node running, explore these topics:

Node Configuration

Learn about advanced node configuration options

Creating Governance

Deep dive into governance creation and management

Managing Subjects

Learn how to create and manage subjects

Database Integration

Implement a persistent database backend

Common Issues

Ensure you have Rust 1.67 or later installed:
rustc --version
Update Rust if needed:
rustup update
Make sure you’re using the same key pair for both signing and verification. The node key pair should be consistent with the settings.
Ensure your async runtime is configured correctly and the node is running. The notification channel may be blocked if the node hasn’t started processing.

Additional Resources

Build docs developers (and LLMs) love