๐ณ libcps - Robonomics CPS Library & CLI
March 8, 2026 ยท View on GitHub
A comprehensive Rust library and command-line interface for managing hierarchical Cyber-Physical Systems on the Robonomics blockchain.
๐ฆ Packages
This crate provides two components:
1. libcps (Library)
A reusable library for building applications that interact with the Robonomics CPS pallet.
2. cps (CLI Binary)
A beautiful command-line interface for quick access to CPS pallet functionality.
โจ Features
- ๐ Multi-algorithm AEAD encryption (XChaCha20-Poly1305, AES-256-GCM, ChaCha20-Poly1305)
- ๐ Dual keypair support (SR25519 for Substrate, ED25519 for IoT/Home Assistant)
- ๐ก MQTT bridge for IoT device integration (optional feature)
- ๐ฒ Hierarchical tree visualization of CPS nodes (CLI)
- โ๏ธ Flexible configuration via environment variables or CLI args
- ๐ Secure by design with proper key management and ECDH key agreement
- ๐ Comprehensive documentation for library API
- ๐ง Type-safe blockchain integration via subxt
- ๐๏ธ Feature flags for flexible dependency management
๐๏ธ Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ libcps CLI โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Commands โ Display โ Crypto โ Blockchain โ MQTT โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ libcps Library โ
โโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโค
โ Cipher โ Types โ Generated Runtime โ
โ - SR25519 โ - NodeData โ - subxt codegen โ
โ - ED25519 โ - NodeId โ - CPS pallet API โ
โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Substrate Node โ โ MQTT Broker โ
โ - CPS Pallet โ โ - rumqttc client โ
โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ฆ Installation
As a Library
Add to your Cargo.toml:
[dependencies]
libcps = "0.1.0"
Feature Flags
The library supports optional feature flags for flexible dependency management:
mqtt- Enables MQTT bridge functionality (enabled by default)cli- Enables CLI binary with colored output (enabled by default)
# Default: all features enabled
[dependencies]
libcps = "0.1.0"
# Library only, without MQTT
[dependencies]
libcps = { version = "0.1.0", default-features = false }
# Library with MQTT only (no CLI)
[dependencies]
libcps = { version = "0.1.0", default-features = false, features = ["mqtt"] }
CLI Tool from Crates.io
cargo install libcps
From Source
# Clone the repository
git clone https://github.com/airalab/robonomics
cd robonomics
# Build the library
cargo build --release --package libcps --lib
# Build the CLI tool
cargo build --release --package libcps --bin cps
# The binary will be at: target/release/cps
Add CLI to PATH (optional)
sudo cp target/release/cps /usr/local/bin/
๐ CLI Quick Start
1. Set up your environment
# Set blockchain endpoint
export ROBONOMICS_WS_URL=ws://localhost:9944
# Set your account (development account for testing)
export ROBONOMICS_SURI=//Alice
# Optional: Set MQTT broker
export ROBONOMICS_MQTT_BROKER=mqtt://localhost:1883
2. Create your first node
# Create a root node
cps create --meta '{"type":"building","name":"HQ"}' --payload '{"status":"online"}'
# Create a child node
cps create --parent 0 --meta '{"type":"room","name":"Server Room"}' --payload '{"temp":"22C"}'
3. View your CPS tree
cps show 0
Output:
[*] CPS Node ID: 0
|-- [O] Owner: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
|-- [M] Meta: {
"type": "building",
"name": "HQ"
}
`-- [P] Payload: {
"status": "online"
}
๐ Commands
show <node_id>
Display node information and its children in a beautiful tree format.
# Show node 0
cps show 0
# Show node with decryption attempt
cps show 5 --decrypt
create
Create a new node (root or child).
# Create root node
cps create --meta '{"type":"sensor"}' --payload '22.5C'
# Create child node
cps create --parent 0 --payload 'operational data'
# Create with encryption (SR25519, default)
cps create --parent 0 --payload 'secret data' --receiver-public <RECEIVER_ADDRESS>
# Create with ED25519 encryption
cps create --parent 0 --payload 'secret data' --receiver-public <RECEIVER_ADDRESS> --scheme ed25519
# Create with specific cipher
cps create --parent 0 --payload 'secret data' --receiver-public <RECEIVER_ADDRESS> --cipher aesgcm256
Options:
--parent <id>: Parent node ID (omit for root node)--meta <data>: Metadata (configuration data)--payload <data>: Payload (operational data)--receiver-public <address>: Receiver public key or SS58 address for encryption (required to encrypt data)--cipher <algorithm>: Encryption algorithm (xchacha20, aesgcm256, chacha20) [default: xchacha20]--scheme <type>: Cryptographic scheme (sr25519, ed25519) [default: sr25519]
set-meta <node_id> <data>
Update node metadata.
# Update metadata
cps set-meta 5 '{"name":"Updated Sensor"}'
# Update with encryption
cps set-meta 5 'private config' --receiver-public <RECEIVER_ADDRESS>
# Update with ED25519 encryption
cps set-meta 5 'private config' --receiver-public <RECEIVER_ADDRESS> --scheme ed25519
set-payload <node_id> <data>
Update node payload (operational data).
# Update temperature reading
cps set-payload 5 '23.1C'
# Update with encryption
cps set-payload 5 'encrypted telemetry' --receiver-public <RECEIVER_ADDRESS>
# Update with ED25519 and AES-GCM
cps set-payload 5 'encrypted telemetry' --receiver-public <RECEIVER_ADDRESS> --scheme ed25519 --cipher aesgcm256
move <node_id> <new_parent_id>
Move a node to a new parent.
# Move node 5 under node 3
cps move 5 3
Features:
- Automatic cycle detection (prevents moving a node under its own descendant)
- Path validation
remove <node_id>
Delete a node (must have no children).
# Remove node with confirmation
cps remove 5
# Remove without confirmation
cps remove 5 --force
mqtt subscribe <topic> <node_id>
Subscribe to MQTT topic and update node payload with received messages.
# Subscribe to sensor data
cps mqtt subscribe "sensors/temp01" 5
# Subscribe with encryption (SR25519)
cps mqtt subscribe "sensors/temp01" 5 --receiver-public <RECEIVER_ADDRESS>
# Subscribe with ED25519 encryption (Home Assistant compatible)
cps mqtt subscribe "homeassistant/sensor/temp" 5 --receiver-public <RECEIVER_ADDRESS> --scheme ed25519
# Subscribe with specific cipher
cps mqtt subscribe "sensors/temp01" 5 --receiver-public <RECEIVER_ADDRESS> --cipher aesgcm256
Behavior:
- Connects to MQTT broker
- Subscribes to specified topic
- On each message: updates node payload
- Displays colorful logs for each update
mqtt publish <topic> <node_id>
Monitor node payload and publish changes to MQTT topic using event-driven architecture.
# Publish node changes
cps mqtt publish "actuators/valve01" 10
Behavior:
- Event-driven monitoring (subscribes to blockchain events)
- Only queries and publishes when payload actually changes
- Automatically decrypts encrypted payloads
See MQTT Bridge section for detailed technical implementation.
โ๏ธ Configuration
Environment Variables
# Blockchain connection
export ROBONOMICS_WS_URL=ws://localhost:9944
# Account credentials
export ROBONOMICS_SURI=//Alice
# Or use a seed phrase:
# export ROBONOMICS_SURI="your twelve word seed phrase here goes like this"
# MQTT configuration
export ROBONOMICS_MQTT_BROKER=mqtt://localhost:1883
export ROBONOMICS_MQTT_USERNAME=myuser
export ROBONOMICS_MQTT_PASSWORD=mypass
export ROBONOMICS_MQTT_CLIENT_ID=cps-cli
CLI Arguments (override environment variables)
cps --ws-url ws://localhost:9944 \
--suri //Alice \
--mqtt-broker mqtt://localhost:1883 \
--mqtt-username myuser \
--mqtt-password mypass \
show 0
๐ Library Usage
Quick Start
This example shows the core node-oriented operations: creating nodes, setting metadata and payload, and visualizing the tree structure.
use libcps::blockchain::{Client, Config};
use libcps::node::{Node, NodeData};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Connect to blockchain
let config = Config {
ws_url: "ws://localhost:9944".to_string(),
suri: Some("//Alice".to_string()),
};
let client = Client::new(&config).await?;
// Create a root node with metadata and payload
let meta: NodeData = r#"{"type":"building","name":"HQ"}"#.into();
let payload: NodeData = r#"{"status":"online"}"#.into();
let root_node = Node::create(&client, None, Some(meta), Some(payload)).await?;
println!("Created root node: {}", root_node.id());
// Create a child node
let child_meta: NodeData = r#"{"type":"room","name":"Server Room"}"#.into();
let child_payload: NodeData = r#"{"temp":"22C"}"#.into();
let child_node = Node::create(&client, Some(root_node.id()), Some(child_meta), Some(child_payload)).await?;
println!("Created child node: {}", child_node.id());
// Update node metadata
let new_meta: NodeData = r#"{"type":"room","name":"Server Room","updated":true}"#.into();
child_node.set_meta(Some(new_meta)).await?;
// Update node payload
let new_payload: NodeData = r#"{"temp":"23.5C"}"#.into();
child_node.set_payload(Some(new_payload)).await?;
// Query and display node information
let info = root_node.query().await?;
println!("Node {} has {} children", info.id, info.children.len());
Ok(())
}
Data Types
use libcps::node::{NodeData, NodeId};
use libcps::crypto::EncryptionAlgorithm;
// Create plain data (unencrypted)
let meta = NodeData::from("sensor config");
let meta_bytes = NodeData::from(vec![1, 2, 3]);
// Create encrypted data from cipher output
let encrypted_msg = cipher.encrypt(plaintext, &receiver_public, EncryptionAlgorithm::XChaCha20Poly1305)?;
let encrypted_bytes = encrypted_msg.encode();
let payload = NodeData::aead_from(encrypted_bytes);
๐ Encryption
The library supports multiple cryptographic schemes and AEAD encryption algorithms with robust key derivation and self-describing message format.
Cryptographic Schemes
Two cryptographic schemes are supported for ECDH key agreement:
| Feature | SR25519 | ED25519 |
|---|---|---|
| Curve | Ristretto255 | Curve25519 (via X25519) |
| ECDH | Ristretto255 scalar multiplication | ED25519 โ X25519 |
| Best For | Substrate blockchain operations | IoT devices, Home Assistant |
| Compatibility | Native to Polkadot ecosystem | Standard ED25519 implementations |
| Key Agreement | scalar * point on Ristretto255 | ED25519 โ Curve25519 โ X25519 |
SR25519 (Default - Substrate Native)
- Uses Ristretto255 curve for ECDH
- Native to Substrate/Polkadot ecosystem
- Best for: Substrate blockchain operations
- Key agreement: Ristretto255 scalar multiplication
ED25519 (IoT Compatible)
- Uses X25519 ECDH (ED25519 โ Curve25519 conversion)
- Compatible with standard ED25519 implementations
- Best for: IoT devices, Home Assistant integration, standard cryptography
- Key agreement: ED25519 โ Curve25519 โ X25519
Encryption Algorithms
Three AEAD ciphers are supported:
-
XChaCha20-Poly1305 (Default)
- 24-byte nonce (collision-resistant)
- ~680 MB/s software performance
- Best for: General purpose, portable
-
AES-256-GCM
- 12-byte nonce
- ~2-3 GB/s with AES-NI hardware acceleration
- Best for: High throughput with hardware support
-
ChaCha20-Poly1305
- 12-byte nonce
- ~600 MB/s software performance
- Best for: Portable performance without hardware acceleration
How it works
-
Key Derivation (ECDH + HKDF)
- For SR25519: Derive shared secret using Ristretto255 ECDH
- For ED25519: Derive shared secret using X25519 ECDH
- Apply HKDF-SHA256 with algorithm-specific info string
-
Encryption (AEAD)
- Encrypt data with derived 32-byte key
- Generate random nonce per message (size varies by algorithm)
- Add authentication tag (AEAD)
-
Self-Describing Message Format
The encrypted message uses SCALE codec for efficient binary serialization on the blockchain. The message format is defined as a versioned Rust enum:
pub enum EncryptedMessage { V1 { algorithm: EncryptionAlgorithm, // XChaCha20Poly1305, AesGcm256, or ChaCha20Poly1305 from: [u8; 32], // Sender's 32-byte public key nonce: Vec<u8>, // 24 bytes for XChaCha20, 12 for AES-GCM/ChaCha20 ciphertext: Vec<u8>, // Encrypted data with authentication tag } }The message is serialized using SCALE codec (Simple Concatenated Aggregate Little-Endian), the native encoding format for Substrate blockchains, providing:
- Blockchain efficiency: Compact binary format minimizes on-chain storage costs
- Automatic algorithm detection: Receiver knows which cipher to use from the enum
- Sender identification: The
fromfield contains sender's raw 32-byte public key - Version compatibility: Enum variants enable future protocol upgrades
- Type safety: Compile-time guarantee of message structure validity with Encode/Decode derives
- Future-proof: New versions can be added as additional enum variants (e.g.,
V2 { ... }) - Native integration: SCALE codec is the standard for all Substrate/Polkadot data
Key Derivation (HKDF-SHA256)
The encryption scheme uses HKDF (RFC 5869) for deriving encryption keys from shared secrets:
Process:
-
ECDH Key Agreement
- SR25519: Ristretto255 scalar multiplication
- ED25519: X25519 (ED25519 โ Curve25519 โ X25519)
- Result: 32-byte shared secret
-
HKDF Extract
salt = "robonomics-network" (constant, for domain separation) PRK = HMAC-SHA256(salt, shared_secret) -
HKDF Expand
info = algorithm-specific string: - "robonomics-cps-xchacha20poly1305" - "robonomics-cps-aesgcm256" - "robonomics-cps-chacha20poly1305" OKM = HMAC-SHA256(PRK, info)[0..32]
Security Properties:
- Domain Separation: Keys bound to Robonomics network context
- Algorithm Binding: Different algorithms produce independent keys
- Key Independence: Each (shared_secret, algorithm) pair โ unique key
- Security Enhancement: Constant salt strengthens key derivation even with low-entropy secrets
๐ก MQTT Bridge
The MQTT bridge enables seamless IoT integration with real-time, event-driven synchronization. The bridge functionality is available both as a CLI command and as a library API.
Library API
The MQTT bridge can be used programmatically from your Rust applications:
use libcps::{mqtt, blockchain::Config};
// Subscribe Bridge: MQTT โ Blockchain
// Using Config method API
mqtt_config.subscribe(
&blockchain_config,
None, // Optional encryption cipher
"sensors/temp", // MQTT topic
1, // Node ID
None, // Optional receiver public key
None, // Optional message handler callback
).await?;
// Publish Bridge: Blockchain โ MQTT
// Using Config method API
mqtt_config.publish(
&blockchain_config,
None, // Optional cipher for decryption
"actuators/status", // MQTT topic
1, // Node ID
None, // Optional publish handler callback
).await?;
See examples/mqtt_bridge.rs for a complete working example.
Configuration File
You can manage multiple bridges using a TOML configuration file. This is ideal for running multiple subscribe and publish bridges concurrently.
CLI Usage
# Start all bridges from config file
cps mqtt start -c mqtt_config.toml
# With custom config path
cps mqtt start --config /etc/cps/mqtt-bridge.toml
Configuration File Format
# MQTT Broker Configuration
broker = "mqtt://localhost:1883"
username = "myuser" # Optional
password = "mypass" # Optional
client_id = "cps-bridge" # Optional
# Blockchain Configuration
[blockchain]
ws_url = "ws://localhost:9944"
suri = "//Alice"
# Subscribe Topics (MQTT โ Blockchain)
[[subscribe]]
topic = "sensors/temperature"
node_id = 5
[[subscribe]]
topic = "sensors/humidity"
node_id = 6
receiver_public = "5GrwvaEF..." # Optional encryption
cipher = "xchacha20" # Optional
scheme = "sr25519" # Optional
# Publish Topics (Blockchain โ MQTT)
[[publish]]
topic = "actuators/valve01"
node_id = 10
[[publish]]
topic = "actuators/fan"
node_id = 11
# Publish with decryption (reads encrypted blockchain data, publishes decrypted to MQTT)
# Algorithm and scheme are auto-detected from the encrypted data
[[publish]]
topic = "decrypted/sensor/data"
node_id = 13
decrypt = true
See examples/mqtt_config.toml for a complete example.
Library Usage
use libcps::mqtt::Config;
// Load config from file
let config = Config::from_file("mqtt_config.toml")?;
// Start all bridges
config.start().await?;
Subscribe: MQTT โ Blockchain
Subscribe to MQTT topics and automatically update blockchain node payload with received messages.
CLI Usage
# Basic subscription
cps mqtt subscribe "sensors/temperature" 5
# With SR25519 encryption (default)
cps mqtt subscribe "sensors/temperature" 5 \
--receiver-public 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
# With ED25519 encryption (Home Assistant compatible)
cps mqtt subscribe "homeassistant/sensor/temperature" 5 \
--receiver-public 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY \
--scheme ed25519
# With AES-GCM cipher
cps mqtt subscribe "sensors/temperature" 5 \
--receiver-public 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY \
--cipher aesgcm256
Library Usage
use libcps::{mqtt, blockchain::Config};
// Create a custom message handler for logging
let handler = Box::new(|topic: &str, payload: &[u8]| {
println!("๐ฅ Received on {}: {:?}", topic, payload);
});
// Using Config method API
mqtt_config.subscribe(
&blockchain_config,
None, // No encryption
"sensors/temp",
1, // node_id
None, // No receiver public key
Some(handler), // Custom message handler
).await?;
Flow:
MQTT Topic โ CPS CLI โ Blockchain Node
โ โ โ
"22.5C" Receive Update Payload
(encrypted if configured)
Publish: Blockchain โ MQTT
Monitor blockchain node for payload changes and publish to MQTT topic in real-time using event-driven architecture.
CLI Usage
# Basic publishing
cps mqtt publish "actuators/valve" 10
# With decryption (auto-detects algorithm from encrypted data)
cps mqtt publish "sensors/encrypted" 10 --decrypt
# With custom broker configuration
cps mqtt publish "actuators/valve" 10 \
--mqtt-broker mqtt://broker.example.com:1883 \
--mqtt-username myuser \
--mqtt-password mypass
Library Usage
use libcps::{mqtt, blockchain::Config, crypto::Cipher};
// Create cipher for decryption (optional)
let cipher = Cipher::new(
"//Alice".to_string(),
crypto::CryptoScheme::Sr25519
)?;
// Create a custom publish handler for logging
let handler = Box::new(|topic: &str, block_num: u32, data: &str| {
println!("๐ค Published to {} at block #{}: {}", topic, block_num, data);
});
// Using Config method API with decryption
mqtt_config.publish(
&blockchain_config,
Some(&cipher), // Optional cipher for decryption
"actuators/status",
1, // node_id
Some(handler), // Custom publish handler
).await?;
Technical Implementation:
- Subscribes to finalized blockchain blocks
- Monitors
PayloadSetevents for target node - Only queries and publishes when payload actually changes (event-driven)
- No polling overhead - reacts to blockchain events in real-time
- Publishes to MQTT with QoS 0 (At Most Once)
- Background event loop for MQTT auto-reconnection
Flow:
Blockchain PayloadSet Event โ Detect Change โ Query Node โ Publish to MQTT
โ โ โ โ
(detected via event) (node_id match) (at block #) (changed data)
Example Output
Subscribe Command:
[~] Connecting to MQTT broker...
[+] Connected to mqtt://localhost:1883
[i] Subscribed to topic: sensors/temp01
[~] Listening for messages...
[2025-12-04 10:30:15] Received: 22.5C
[i] Encrypting with XChaCha20-Poly1305 using SR25519
[+] Updated node 5 payload
[2025-12-04 10:30:45] Received: 23.1C
[i] Encrypting with XChaCha20-Poly1305 using SR25519
[+] Updated node 5 payload
Publish Command:
[~] Connecting to blockchain...
[+] Connected to ws://localhost:9944
[~] Connecting to MQTT broker localhost:1883...
[+] Connected to mqtt://localhost:1883
[i] Monitoring node 10 payload on each block...
[2025-12-04 10:31:20] Published to actuators/valve01 at block #1234: open
[2025-12-04 10:31:50] Published to actuators/valve01 at block #1240: closed
Authentication
Configure MQTT credentials via environment variables or CLI flags:
# Environment variables
export ROBONOMICS_MQTT_BROKER=mqtt://broker.example.com:1883
export ROBONOMICS_MQTT_USERNAME=myuser
export ROBONOMICS_MQTT_PASSWORD=mypassword
export ROBONOMICS_MQTT_CLIENT_ID=cps-client-01
# Or via CLI flags
cps mqtt subscribe "topic" 5 \
--mqtt-broker mqtt://broker.example.com:1883 \
--mqtt-username myuser \
--mqtt-password mypassword
Integration Examples
Home Assistant Integration
# Subscribe to Home Assistant sensor (ED25519 compatible)
cps mqtt subscribe "homeassistant/sensor/living_room/temperature" 100 \
--receiver-public <HOME_ASSISTANT_PUBLIC_KEY> \
--scheme ed25519 \
--cipher aesgcm256
# Publish to Home Assistant actuator
cps mqtt publish "homeassistant/switch/kitchen/light" 101
Industrial IoT
# Monitor encrypted machine telemetry
cps mqtt subscribe "factory/line1/cnc001/telemetry" 200 \
--receiver-public <MACHINE_PUBLIC_KEY> \
--cipher xchacha20
# Publish control commands
cps mqtt publish "factory/line1/controller/commands" 201
Smart Building
# Create building hierarchy
cps create --meta '{"type":"building","name":"HQ"}' # Node 0
cps create --parent 0 --meta '{"type":"floor","number":1}' # Node 1
cps create --parent 1 --meta '{"type":"room","name":"Server"}' # Node 2
# Bridge temperature sensor
cps mqtt subscribe "building/floor1/server-room/temp" 2
# Monitor and publish HVAC status
cps mqtt publish "building/floor1/server-room/hvac" 2
Error Handling
The MQTT bridge handles various error scenarios gracefully:
- Connection Failures: Auto-reconnect with 5-second delay
- Invalid Messages: Logged and skipped
- Blockchain Errors: Logged with timestamps
- Encryption Errors: Descriptive error messages
- Graceful Shutdown: Background tasks cleaned up on exit
Performance Considerations
- Event-Driven: No unnecessary blockchain queries
- Efficient: Only processes blocks with relevant events
- Low Latency: Real-time event detection
- Resource Efficient: Minimal memory footprint
- Scalable: Multiple instances can run simultaneously
๐ฏ Use Cases
1. IoT Sensor Network
# Create building structure
cps create --meta '{"type":"building"}'
cps create --parent 0 --meta '{"type":"floor","number":1}'
cps create --parent 1 --meta '{"type":"room","name":"Server Room"}'
# Bridge sensor data
cps mqtt subscribe "sensors/room1/temp" 2
cps mqtt subscribe "sensors/room1/humidity" 2
2. Smart Home Automation
# Create home hierarchy
cps create --meta '{"type":"home"}'
cps create --parent 0 --meta '{"type":"room","name":"Kitchen"}'
cps create --parent 1 --meta '{"type":"device","name":"Smart Light"}'
# Control devices
cps mqtt publish "devices/kitchen/light/state" 2
3. Industrial Monitoring
# Create factory structure
cps create --meta '{"type":"factory"}'
cps create --parent 0 --meta '{"type":"line","name":"Assembly Line 1"}'
cps create --parent 1 --meta '{"type":"machine","id":"CNC-001"}'
# Monitor machine data with encryption
cps mqtt subscribe "machines/cnc001/telemetry" 2 --receiver-public <RECEIVER_ADDRESS>
๐ ๏ธ Development
Project Structure
tools/libcps/
โโโ Cargo.toml # Dependencies and features (uses robonomics-runtime-subxt-api)
โโโ README.md # This file
โโโ DEVELOPMENT.md # Developer guide
โโโ src/
โโโ lib.rs # Library entry point with module exports
โโโ main.rs # CLI entry point
โโโ node.rs # Node-oriented API with CPS type definitions
โโโ blockchain/ # Blockchain client and connection
โ โโโ mod.rs
โ โโโ client.rs
โโโ commands/ # CLI command implementations
โ โโโ mod.rs
โ โโโ show.rs
โ โโโ create.rs
โ โโโ set_meta.rs
โ โโโ set_payload.rs
โ โโโ move_node.rs
โ โโโ remove.rs
โ โโโ mqtt.rs
โโโ crypto/ # Encryption utilities
โ โโโ mod.rs # Documentation and re-exports
โ โโโ types.rs # CryptoScheme, EncryptionAlgorithm, EncryptedMessage
โ โโโ cipher.rs # Cipher implementation
โโโ mqtt/ # MQTT bridge (optional feature)
โ โโโ mod.rs
โ โโโ bridge.rs
โโโ display/ # Pretty CLI output
โโโ mod.rs
โโโ tree.rs
Building
cargo build --package libcps
Testing
cargo test --package libcps
Generating Blockchain Types
Type-safe blockchain interactions are automatically generated using the
robonomics-runtime-subxt-api crate. This crate:
- Extracts metadata from the robonomics runtime at build time
- Saves metadata to
$OUT_DIR/metadata.scale - subxt macro reads the metadata and generates type-safe APIs at compile time
No external tools required! Just build the project:
cargo build -p libcps
The generated types are always in sync with the runtime dependency version. For more details, see the subxt-api documentation.
๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
๐ License
Apache-2.0
๐ Links
๐ก Tips
- Use
//Alice,//Bob, etc. for development accounts - Always backup your seed phrase in production
- Test encryption with development keys first
- Monitor MQTT bridge logs for debugging
- Use
--helpon any command for more details
๐ Troubleshooting
Connection Failed
# Check if node is running
curl -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "system_health"}' http://localhost:9944
# Try default WebSocket URL
cps --ws-url ws://127.0.0.1:9944 show 0
Account Not Found
# Make sure SURI is set
export ROBONOMICS_SURI=//Alice
# Or pass it directly
cps --suri //Alice create --meta '{"test":true}'
MQTT Connection Issues
# Test MQTT broker
mosquitto_pub -h localhost -t test -m "hello"
# Check broker URL format
export ROBONOMICS_MQTT_BROKER=mqtt://localhost:1883
Made with โค๏ธ by the Robonomics Team