Skip to content

Cyber-Physical System pallet #405

@akru

Description

@akru

🏗️ Cyber-Physical System Pallet

pallet-robonomics-cps is a Substrate pallet providing an efficient, secure, and minimal hierarchical data model (tree/forest) for representing Cyber-Physical Systems (CPS) on-chain.

🎯 What Can You Build?

  • Smart Homes: Model rooms, devices, sensors as a tree
  • Sensor Networks: Organize monitoring stations and their sensors
  • Robotics: Structure robot fleets with subsystems and components
  • Industrial IoT: Represent factories, production lines, and equipment
  • Any CPS: Flexible enough for any hierarchical cyber-physical system

✨ Key Features

  • O(1) Performance: All operations are constant-time (no recursive traversals)
  • 💾 Compact Storage: SCALE encoding saves 75-87% storage for typical node IDs
  • 🔐 Privacy-Aware: Mix public and encrypted data in the same tree
  • 🎛️ Configurable: Adjust depth limits, children limits, data sizes
  • 🔗 Runtime Hooks: React to payload changes with custom logic
  • 🌲 Forest Support: Multiple independent CPS trees in one registry

📖 Quick Start: Building a Smart Home

Let's build a simple smart home CPS step-by-step:

Home (root)
├─ Kitchen
│   ├─ Smart Lamp
│   └─ Temperature Sensor
└─ Living Room
    └─ Motion Detector

Step 1: Create the Home (Root Node)

use pallet_robonomics_cps::{NodeData, NodeId};
use frame_support::BoundedVec;

// Plain metadata describing the home
let home_meta = NodeData::Plain(
    BoundedVec::try_from(
        br#"{"type":"building","name":"My Smart Home"}"#. to_vec()
    ). unwrap()
);

// Create root node (parent = None)
Cps::create_node(origin, None, Some(home_meta), None)?;
// → Creates NodeId(0)

Step 2: Add Kitchen Room

let kitchen_meta = NodeData::Plain(
    BoundedVec::try_from(
        br#"{"type":"room","name":"Kitchen","floor":1}"#.to_vec()
    ).unwrap()
);

// Create as child of Home
Cps::create_node(origin, Some(NodeId(0)), Some(kitchen_meta), None)?;
// → Creates NodeId(1)

Step 3: Add Smart Lamp with Encrypted Config

use pallet_robonomics_cps::DefaultEncryptedData;

// Public metadata
let lamp_meta = NodeData::Plain(
    BoundedVec::try_from(
        br#"{"type":"device","model":"SmartBulb Pro"}"#.to_vec()
    ).unwrap()
);

// Encrypted WiFi credentials (encrypted off-chain before submission)
let encrypted_config = DefaultEncryptedData::XChaCha20Poly1305(
    BoundedVec::try_from(encrypted_wifi_creds). unwrap()
);
let lamp_payload = NodeData::Encrypted(encrypted_config);

// Create lamp under Kitchen
Cps::create_node(
    origin,
    Some(NodeId(1)), // Kitchen
    Some(lamp_meta),
    Some(lamp_payload)
)?;
// → Creates NodeId(2)

Step 4: Update Sensor Data

// Temperature sensor sending telemetry
let sensor_reading = NodeData::Plain(
    BoundedVec::try_from(
        br#"{"temp_c":22.5,"humidity":45,"timestamp":1733011200}"#.to_vec()
    ).unwrap()
);

Cps::set_payload(origin, NodeId(3), Some(sensor_reading))? ;
// Triggers OnPayloadSet callback if configured

Step 5: Move a Device

// Move lamp from Kitchen to Living Room
Cps::move_node(origin, NodeId(2), NodeId(4))?;
// Automatically updates paths of lamp and all descendants

🏛️ Architecture Overview

┌─────────────────────────────────────────────────────────────┐
│                    USER LAYER                                │
│  Wallet • Mobile App • IoT Gateway • Automation Service     │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       │ Extrinsics (create, update, move)
                       ▼
┌─────────────────────────────────────────────────────────────┐
│                 ON-CHAIN PALLET                              │
│  ┌─────────────┐  ┌──────────────┐  ┌──────────────┐       │
│  │   Nodes     │  │ NodesByParent│  │  RootNodes   │       │
│  │ (main map)  │  │  (index)     │  │  (index)     │       │
│  └─────────────┘  └──────────────┘  └──────────────┘       │
│                                                              │
│  Storage: Tree structure + Plain/Encrypted data blobs       │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       │ RPC Queries (read state)
                       ▼
┌─────────────────────────────────────────────────────────────┐
│                   OFF-CHAIN LOGIC                            │
│  • Fetch tree structure                                      │
│  • Decrypt encrypted nodes (client-side)                     │
│  • Build semantic CPS model                                  │
│  • Control physical devices                                  │
│  • Display UI • Analytics • Automation                       │
└─────────────────────────────────────────────────────────────┘

🗂️ Data Structures Explained

Node Structure

pub struct Node<AccountId, T: Config> {
    parent: Option<NodeId>,              // None = root node
    owner: AccountId,                     // Who controls this node
    path: BoundedVec<NodeId, MaxDepth>, // [grandparent, parent] for O(1) ops
    meta: Option<NodeData>,              // Config, type, name, specs
    payload: Option<NodeData>,           // State, readings, telemetry
}

Example Node:

{
  "parent": 1,
  "owner": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
  "path": [0, 1],
  "meta": {
    "Plain": "{\"type\":\"sensor\",\"name\":\"Temp-01\"}"
  },
  "payload": {
    "Plain": "{\"temp_c\":23.1,\"timestamp\":1733011200}"
  }
}

NodeData: Plain vs Encrypted

Plain Data Encrypted Data
NodeData::Plain(bytes) NodeData::Encrypted(DefaultEncryptedData::XChaCha20Poly1305(ciphertext))
Readable by everyone Only readable with decryption keys (off-chain)
Use for: public specs, model numbers Use for: WiFi passwords, API keys, personal data
Example: {"type":"device"} Example: 0x8f3a2... (encrypted blob)

Encryption Algorithms Supported

pub enum DefaultEncryptedData {
    XChaCha20Poly1305(BoundedVec<u8, MaxDataSize>),  // Recommended
    AesGcm256(BoundedVec<u8, MaxDataSize>),           // Hardware accelerated
    ChaCha20Poly1305(BoundedVec<u8, MaxDataSize>),    // Software optimized
}

🔧 Core Operations (Extrinsics)

1️⃣ create_node - Add Node to Tree

Signature:

fn create_node(
    origin: OriginFor<T>,
    parent_id: Option<NodeId>,
    meta: Option<NodeData<T::EncryptedData>>,
    payload: Option<NodeData<T::EncryptedData>>,
) -> DispatchResult

Use Cases:

  • parent_id = None → Create a new CPS tree (root node)
  • parent_id = Some(id) → Add child to existing node

Rules:

  • Child inherits parent's owner
  • Checks max depth and max children limits
  • Updates Nodes, NodesByParent, and RootNodes storage

2️⃣ set_meta - Update Node Metadata

Signature:

fn set_meta(
    origin: OriginFor<T>,
    node_id: NodeId,
    meta: Option<NodeData<T::EncryptedData>>,
) -> DispatchResult

Example:

// Change device name
let new_meta = NodeData::Plain(
    BoundedVec::try_from(br#"{"type":"device","name":"Kitchen Lamp Pro"}"#.to_vec()).unwrap()
);
Cps::set_meta(origin, NodeId(2), Some(new_meta))? ;

3️⃣ set_payload - Update Node Payload

Signature:

fn set_payload(
    origin: OriginFor<T>,
    node_id: NodeId,
    payload: Option<NodeData<T::EncryptedData>>,
) -> DispatchResult

Use Cases:

  • Sensors pushing telemetry
  • Devices updating their state
  • Configuration updates

Special: Triggers OnPayloadSet callback for custom runtime logic


4️⃣ move_node - Reorganize Tree

Signature:

fn move_node(
    origin: OriginFor<T>,
    node_id: NodeId,
    new_parent_id: NodeId,
) -> DispatchResult

Example:

// Move sensor from Kitchen to Living Room
Cps::move_node(origin, NodeId(3), NodeId(4))?;

Safety Guarantees:

  • ✅ Prevents cycles (O(1) path check)
  • ✅ Enforces same owner for node and new parent
  • ✅ Updates all descendant paths automatically
  • ✅ Maintains index consistency

5️⃣ delete_node - Remove Node

Signature:

fn delete_node(
    origin: OriginFor<T>,
    node_id: NodeId,
) -> DispatchResult

Rules:

  • ❌ Cannot delete nodes with children (safety)
  • ✅ Cleans up all indexes (NodesByParent, RootNodes)
  • ✅ Only owner can delete

🔐 Security Model

Ownership-Based Access Control

┌────────────────────────────────────────────────┐
│  Every node has an OWNER                       │
│  Only the owner can:                           │
│    • Update metadata (set_meta)                │
│    • Update payload (set_payload)              │
│    • Move node (move_node)                     │
│    • Delete node (delete_node)                 │
│    • Create children (create_node)             │
└────────────────────────────────────────────────┘

Invariants Enforced

No Cycles: Path-based O(1) cycle detection
Ownership Inheritance: Children always have parent's owner
Index Consistency: All storage items stay synchronized
Safe Deletion: Nodes with children cannot be deleted
Depth Limits: Tree depth never exceeds MaxTreeDepth


🎭 Privacy: Plain vs Encrypted

Visibility Comparison

Scenario Plain Meta Encrypted Meta Who Can See?
Public device specs Everyone
WiFi credentials Owner only (with key)
Sensor model number Everyone
Personal health data Owner only (with key)
Room names Everyone
Access codes Owner only (with key)

Mixed Privacy Example

// Public: What the device is
let public_meta = NodeData::Plain(
    BoundedVec::try_from(br#"{"type":"camera","model":"SecureCam 2000"}"#.to_vec()).unwrap()
);

// Private: How to access it
let private_payload = NodeData::Encrypted(
    DefaultEncryptedData::XChaCha20Poly1305(encrypted_rtsp_url)
);

Cps::create_node(origin, Some(parent), Some(public_meta), Some(private_payload))?;

Result:

  • 🌐 Everyone sees: "This is a SecureCam 2000"
  • 🔒 Only owner with key sees: rtsp://admin:[email protected]:554/stream

📊 Storage Layout

Three Storage Items

1.  Nodes: StorageMap<NodeId, Node>
   ├─ NodeId(0) → Node { parent: None, owner: Alice, ...  }
   ├─ NodeId(1) → Node { parent: Some(0), owner: Alice, ... }
   └─ NodeId(2) → Node { parent: Some(1), owner: Alice, ... }

2. NodesByParent: StorageMap<NodeId, Vec<NodeId>>
   ├─ NodeId(0) → [1, 4]        // Home has Kitchen and Living Room
   ├─ NodeId(1) → [2, 3]        // Kitchen has Lamp and Sensor
   └─ NodeId(4) → [5]           // Living Room has Motion Detector

3.  RootNodes: StorageValue<Vec<NodeId>>
   └─ [0, 10, 20]               // Three independent CPS trees

Path-Based Performance

Traditional Tree (Recursive):

Cycle Check: O(n) - traverse up the tree
Depth Check: O(n) - count ancestors
Ancestor Test: O(n) - walk up the tree

CPS Pallet (Path-Based):

Cycle Check: O(1) - new_parent. path.contains(node_id)
Depth Check: O(1) - parent.path.len()
Ancestor Test: O(1) - path.contains(ancestor_id)

🔗 Runtime Hooks: OnPayloadSet

Use Cases for Callbacks

pub trait OnPayloadSet<AccountId, EncryptedData> {
    fn on_payload_set(
        node_id: NodeId,
        meta: Option<NodeData<EncryptedData>>,
        payload: Option<NodeData<EncryptedData>>,
    );
}

Example Implementations:

  • 📊 Indexing: Track payload changes for off-chain queries
  • 🔔 Notifications: Trigger alerts when sensors update
  • 📈 Analytics: Collect metrics about system usage
  • 🤖 Automation: Chain actions (e.g., sensor triggers actuator)
  • 📝 Audit Trail: Log all payload modifications

Configuration:

// No callback
type OnPayloadSet = ();

// Single handler
type OnPayloadSet = MyHandler;

// Multiple handlers
type OnPayloadSet = (IndexerHandler, NotificationHandler, AnalyticsHandler);

🔑 Proxy-Based Access Delegation

The pallet integrates with Substrate's pallet-proxy to enable delegated access without transferring ownership. Think of it as giving someone a key to your house, but not the deed.

🎯 Why Use Proxies?

┌─────────────────────────────────────────────────────────┐
│                    OWNER (Alice)                        │
│              Controls Everything                        │
└────────────┬───────────────┬────────────────────────────┘
             │               │
             │ grants proxy  │ grants proxy
             │               │
      ┌──────▼──────┐   ┌───▼──────────┐
      │   Gateway   │   │  Engineer    │
      │   (Bob)     │   │  (Carol)     │
      └──────┬──────┘   └───┬──────────┘
             │               │
             │ can update    │ can reorganize
             │ sensor data   │ node structure
             │               │
      ┌──────▼───────────────▼──────────┐
      │      Alice's CPS Tree           │
      │  ✅ Ownership stays with Alice  │
      │  ✅ Access is revocable         │
      │  ✅ Actions are auditable       │
      └─────────────────────────────────┘

Use Cases:

  • 🌡️ IoT Device Management: Gateways update sensor data without owner keys
  • 👥 Team Collaboration: Multiple engineers manage department nodes
  • 🤖 Automated Systems: Bots trigger updates based on external events
  • Temporary Access: Time-limited permissions for contractors
  • 🏢 Hierarchical Delegation: Department leads manage their subtrees

🌐 User Story: IoT Sensor Network

Scenario: Alice owns temperature sensors. She wants her IoT gateway to update readings without risking her account.

The Setup

┌─────────────────────────────────────────────────────────┐
│  Step 1: Alice creates sensor hierarchy                │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   Building A Sensors (NodeId 0)                        │
│   └── Room 101 Temp Sensor (NodeId 1)                  │
│       📊 Current: 22.5°C                                │
│                                                         │
│   Owner: Alice (5GrwvaEF...)                            │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  Step 2: Alice grants proxy to Gateway                 │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   Proxy::add_proxy(                                     │
│     owner: Alice,                                       │
│     delegate: Gateway,                                  │
│     type: CpsWrite(None),  ← Only CPS operations       │
│     delay: 0 blocks        ← Immediately active        │
│   )                                                     │
│                                                         │
│   ✅ Gateway can now update CPS nodes                   │
│   ❌ Gateway CANNOT transfer Alice's funds              │
│   ❌ Gateway CANNOT change ownership                    │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  Step 3: Gateway updates sensor (acting as Alice)      │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   Proxy::proxy(                                         │
│     signer: Gateway,                                    │
│     real_account: Alice,                                │
│     call: Cps::set_payload(NodeId(1), "23.1°C")        │
│   )                                                     │
│                                                         │
│   📊 Sensor now shows: 23.1°C                           │
│   👤 Owner still: Alice                                 │
│   📝 Event logged: Gateway acted on Alice's behalf     │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  Step 4: Alice revokes access when done                │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   Proxy::remove_proxy(Alice, Gateway, ...)             │
│                                                         │
│   ❌ Gateway can no longer update nodes                 │
│   ✅ Alice retains full control                         │
└─────────────────────────────────────────────────────────┘

🎭 Proxy Types Comparison

Proxy Type Scope Best For Security Level
CpsWrite(None) All CPS nodes Trusted gateways, team members 🟡 Medium - can modify all nodes
CpsWrite(Some(NodeId)) Specific node + children Contractors, scoped automation 🟢 High - limited to subtree
Time-delayed (100 blocks) Any of above + waiting period High-value operations 🟢 High - review before activation

📊 Access Control Visualization

Without Proxy (Traditional)

┌──────────────┐
│ Alice's Keys │  ← Single point of failure
└──────┬───────┘
       │
       ├─→ Update Sensor 1
       ├─→ Update Sensor 2  
       ├─→ Transfer Funds  } All or nothing access
       ├─→ Change Settings }
       └─→ Delete Nodes    }

With CPS Proxy

┌──────────────┐
│ Alice's Keys │  ← Stays secure
└──────┬───────┘
       │
       ├─→ Gateway Proxy ────→ ✅ Update Sensors only
       │                       ❌ Cannot transfer funds
       │                       ❌ Cannot change ownership
       │
       ├─→ Engineer Proxy ───→ ✅ Reorganize tree structure
       │                       ❌ Cannot delete root nodes
       │                       ❌ Cannot access other departments
       │
       └─→ Bot Proxy ────────→ ✅ Set alerts/notifications
                               ❌ Cannot create new nodes
                               ❌ Limited to monitoring subtree

🔐 Security Features

┌───────────────────────────────────────────────────────────┐
│              PROXY SECURITY GUARANTEES                    │
├───────────────────────────────────────────────────────────┤
│                                                           │
│  🎯 Type Safety                                           │
│     ProxyType::CpsWrite restricts to CPS operations only  │
│                                                           │
│  🌳 Node-Level Granularity                                │
│     CpsWrite(Some(id)) limits to specific subtree        │
│                                                           │
│  👤 Ownership Preserved                                    │
│     All operations maintain original owner                │
│                                                           │
│  🔄 Revocable                                              │
│     Owner can remove proxy access anytime                 │
│                                                           │
│  📝 Auditable                                              │
│     All proxy actions recorded in blockchain events       │
│                                                           │
│  🚫 No Privilege Escalation                               │
│     Proxies cannot grant permissions to others            │
│                                                           │
│  ⏰ Time-Locked Safety                                     │
│     Delayed proxies allow review before activation        │
│                                                           │
└───────────────────────────────────────────────────────────┘

🎯 Common Patterns

Pattern 1: IoT Gateway (Full CPS Access)

Owner grants: CpsWrite(None), delay=0
├─ Gateway can: Update all sensor payloads
├─ Gateway can: Create new device nodes
└─ Gateway cannot: Transfer funds or delete root nodes

Pattern 2: Contractor (Scoped Access)

Owner grants: CpsWrite(Some(NodeId(5))), delay=0
├─ Contractor can: Update node 5 and children
├─ Contractor can: Create nodes under node 5
└─ Contractor cannot: Access any other nodes

Pattern 3: Security Audit (Time-Delayed)

Owner grants: CpsWrite(None), delay=100 blocks
├─ Auditor must wait: ~10 minutes (100 blocks)
├─ Owner can review: Delegation before it activates
└─ Owner can cancel: If delegation seems suspicious

Pattern 4: Team Collaboration (Multiple Proxies)

Owner grants multiple:
├─ Engineer A: CpsWrite(Some(NodeId(10)))
├─ Engineer B: CpsWrite(Some(NodeId(20)))
└─ Manager: CpsWrite(None)

Result: Each team member manages their area

🚀 Quick Setup Guide

1️⃣ Define ProxyType in Your Runtime

Required once per runtime configuration. See full code example in official docs.

pub enum ProxyType {
    CpsWrite(Option<NodeId>),  // CPS-only access
    // ... other types
}

2️⃣ Grant Proxy Access

// Owner grants access to delegate
Proxy::add_proxy(
    RuntimeOrigin::signed(owner),
    delegate_account,
    ProxyType::CpsWrite(None),  // or Some(node_id) for scoped
    0  // delay in blocks
)

3️⃣ Delegate Performs Action

// Delegate acts on owner's behalf
Proxy::proxy(
    RuntimeOrigin::signed(delegate),
    owner_account,
    None,
    Box::new(RuntimeCall::Cps(Call::set_payload { ...  }))
)

4️⃣ Revoke When Done

// Owner removes access
Proxy::remove_proxy(
    RuntimeOrigin::signed(owner),
    delegate_account,
    ProxyType::CpsWrite(None),
    0
)

💾 Compact Encoding Benefits

Storage Savings with SCALE Compact

Node ID Range Standard Encoding Compact Encoding Savings
0-63 8 bytes 1 byte 87%
64-16,383 8 bytes 2 bytes 75%
16,384-2,097,151 8 bytes 3 bytes 62%

Real-World Impact:

  • Smart home with 50 devices: ~350 bytes saved in node IDs alone
  • Industrial facility with 1,000 sensors: ~7 KB saved
  • City-scale IoT network with 100,000 nodes: ~700 KB saved

🎓 Advanced Example: Industrial IoT

Scenario: Factory Monitoring System

Factory (root)
├─ Production Line A
│   ├─ Robot Arm 1
│   │   ├─ Motor Controller (encrypted diagnostics)
│   │   └─ Gripper Sensor
│   └─ Quality Camera (encrypted RTSP stream)
└─ Production Line B
    ├─ Conveyor Belt
    └─ Weight Sensor (plain telemetry)

Implementation

// 1. Create factory root
let factory_meta = NodeData::Plain(
    BoundedVec::try_from(br#"{"type":"facility","name":"Factory Berlin","location":"DE"}"#.to_vec()).unwrap()
);
Cps::create_node(origin. clone(), None, Some(factory_meta), None)?;
let factory_id = NodeId(0);

// 2. Add production line
let line_meta = NodeData::Plain(
    BoundedVec::try_from(br#"{"type":"production_line","name":"Line A","capacity":1000}"#.to_vec()).unwrap()
);
Cps::create_node(origin.clone(), Some(factory_id), Some(line_meta), None)?;
let line_id = NodeId(1);

// 3. Add robot with encrypted maintenance logs
let robot_meta = NodeData::Plain(
    BoundedVec::try_from(br#"{"type":"robot","model":"KUKA KR 6","serial":"ABC123"}"#.to_vec()).unwrap()
);
let encrypted_logs = NodeData::Encrypted(
    DefaultEncryptedData::AesGcm256(encrypted_maintenance_data)
);
Cps::create_node(origin.clone(), Some(line_id), Some(robot_meta), Some(encrypted_logs))? ;
let robot_id = NodeId(2);

// 4. Robot sends hourly diagnostics
let diagnostics = NodeData::Encrypted(
    DefaultEncryptedData::XChaCha20Poly1305(encrypted_diagnostic_report)
);
Cps::set_payload(origin. clone(), robot_id, Some(diagnostics))?;
// → Triggers OnPayloadSet for monitoring system

// 5. Reorganize: Move robot to Line B
Cps::move_node(origin, robot_id, NodeId(5))?;
// → All descendant paths updated automatically

🧪 Testing Your CPS

Query the Tree

// Get a node
let node = Nodes::<T>::get(NodeId(2)).ok_or(Error::<T>::NodeNotFound)?;
println!("Owner: {:?}", node.owner);
println!("Path: {:?}", node.path);

// Get all children
let children = NodesByParent::<T>::get(NodeId(0));
println!("Children: {:?}", children);

// Get all root nodes
let roots = RootNodes::<T>::get();
println!("Root nodes: {:?}", roots);

// Check if node is ancestor (O(1))
let is_ancestor = node.path.contains(&NodeId(0));

🚀 Getting Started

1. Add to Your Runtime

impl pallet_robonomics_cps::Config for Runtime {
    type RuntimeEvent = RuntimeEvent;
    type MaxTreeDepth = ConstU32<16>;
    type MaxChildrenPerNode = ConstU32<100>;
    type MaxRootNodes = ConstU32<1000>;
    type EncryptedData = pallet_robonomics_cps::DefaultEncryptedData;
    type OnPayloadSet = (); // Or your custom handler
    type WeightInfo = ();
}

2. Create Your First CPS

// In your extrinsic or test
let meta = NodeData::Plain(
    BoundedVec::try_from(b"My First CPS". to_vec()).unwrap()
);
Cps::create_node(origin, None, Some(meta), None)?;

3. Explore the Implementation

📂 Source Code: [frame/cps/src/lib.rs](https://github.com/airalab/robonomics/blob/feat/cps1_0/frame/cps/src/lib. rs)
🌿 Branch: feat/cps1_0


📚 Summary

pallet-robonomics-cps provides:

✅ Hierarchical CPS registry with O(1) performance
✅ Flexible plain/encrypted data storage
✅ Strong ownership-based security model
✅ Configurable limits for safety and scalability
✅ Runtime hooks for extensibility
✅ Compact encoding for storage efficiency
✅ Support for multiple encryption algorithms
✅ Safe tree operations (move, delete) with automatic path updates

Perfect for: Smart homes, sensor networks, robotics, industrial IoT, and any hierarchical cyber-physical system that needs decentralized, privacy-aware on-chain coordination.


Tasks

Pallet

  • Basic functionality
  • Test coverage
  • Benchmarking support
  • Added customizable (defined on runtime level) encrypted payload type
  • Added README and in-code docs
  • Added OnPayloadSet callback support for extensions
  • Added pallet-proxy for node access management
  • Added section into docs for access management (Proxy pallet)

CLI & Library

  • Add CLI application for interaction with pallet
  • Add library to make off-chain part of integration with pallet more simple
  • Add method to send encrypted data
  • Add command to show node data
  • Add MQTT integration using CLI
  • Add acl / proxy support into CLI application / library
  • Add rws support into CLI application / library

Integration tests

  • Added integration tests for access management
  • Added integration tests for data encryption

Indexer

  • Indexing basic support
  • Add indexer documentation and usage examples
  • Add indexer docker image
  • Add indexer integration tests

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions