Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Module: nodalync-cli

Source: Not in spec (application layer)

Overview

Command-line interface for interacting with a Nodalync node. User-facing binary.

Dependencies

  • All nodalync-* crates
  • clap — Argument parsing
  • indicatif — Progress bars
  • colored — Terminal colors

Commands

Identity

# Initialize new identity
nodalync init
> Identity created: ndl1abc123...
> Configuration saved to <data_dir>/config.toml

# Show identity
nodalync whoami
> PeerId: ndl1abc123...
> Public Key: 0x...
> Addresses: /ip4/0.0.0.0/tcp/9000

Content Management

# Publish content
nodalync publish <file> [--price <amount>] [--visibility <private|unlisted|shared>]
> Hashing content...
> Extracting L1 mentions... (23 found)
> Published: a1b2c3d4e5f6...
> Price: 0.10 HBAR
> Visibility: shared

# List local content
nodalync list [--visibility <filter>]
> SHARED (3)
>   a1b2c3d4e5f6... "Research Paper" v3, 0.10 HBAR, 847 queries
>   b7c8d9e0f1a2... "Analysis" v1, 0.05 HBAR, 234 queries
>
> PRIVATE (2)
>   d9e0f1a2b3c4... "Draft Ideas" v4
>   e5f6a7b8c9d0... "Personal Notes" v1

# Update content (new version)
nodalync update <hash> <new-file>
> Previous: a1b2c3d4e5f6... (v1)
> New: b7c8d9e0f1a2... (v2)
> Version root: a1b2c3d4e5f6...

# Show versions
nodalync versions <hash>
> Version root: a1b2c3d4e5f6...
> v1: a1b2c3d4e5f6... (2025-01-15) - shared
> v2: b7c8d9e0f1a2... (2025-01-20) - shared [latest]

# Change visibility
nodalync visibility <hash> --level <private|unlisted|shared>
> Visibility updated: a1b2c3d4e5f6... → shared

# Delete (local only)
nodalync delete <hash>
> Deleted: a1b2c3d4e5f6... (local copy only, provenance preserved)

Discovery & Querying

# Search network
nodalync search "climate change mitigation" [--limit <n>]
> Found 47 results
> [1] b7c8d9e0f1a2... "IPCC Report Summary" by ndl1def... (0.05/query, 847 queries)
>     Preview: Global temperatures have risen 1.1°C since pre-industrial...
> [2] c3d4e5f6a7b8... "Carbon Capture Analysis" by ndl1ghi... (0.12/query, 234 queries)
>     Preview: Current carbon capture technology can sequester...

# Preview content (free)
nodalync preview <hash>
> Title: "IPCC Report Summary"
> Owner: ndl1def...
> Price: 0.05 HBAR
> Queries: 847
> 
> L1 Mentions (5 of 23):
> - Global temperatures have risen 1.1°C since pre-industrial
> - Net-zero by 2050 requires 45% emission reduction by 2030
> - ...

# Query content (paid)
nodalync query <hash>
> Querying b7c8d9e0f1a2...
> Payment: 0.05 HBAR
> Content saved to ./cache/b7c8d9e0f1a2...

Synthesis

# Create L3 insight from sources
nodalync synthesize --sources <hash1>,<hash2>,... --output <file>
> Verifying sources queried... ✓
> Computing provenance (12 roots)...
> L3 hash: f1a2b3c4d5e6...
> 
> Publish now? [y/n/set price]: 0.15
> Published: f1a2b3c4d5e6... (0.15 HBAR, shared)

# Reference external L3 as L0
nodalync reference <l3-hash>
> Referencing a1b2c3d4e5f6... as L0 for future derivations

Economics

# Check balance
nodalync balance
> Protocol Balance: 127.50 HBAR
> Pending Earnings: 4.23 HBAR
> Pending Settlement: 12 payments
>
> Breakdown:
>   Direct queries: 89.20 HBAR
>   Root contributions: 38.30 HBAR

# Earnings by content
nodalync earnings [--content <hash>]
> Top earning content:
>   a1b2c3d4e5f6... "Research Paper": 45.30 HBAR (234 queries)
>   b7c8d9e0f1a2... "Analysis": 23.10 HBAR (462 queries, as root)

# Deposit tokens
nodalync deposit <amount>
> Depositing 50.00 HBAR...
> Transaction: 0x...
> New balance: 177.50 HBAR

# Withdraw tokens
nodalync withdraw <amount>
> Withdrawing 100.00 HBAR...
> Transaction: 0x...
> New balance: 77.50 HBAR

# Force settlement
nodalync settle
> Settling 12 pending payments...
> Batch ID: 0a1b2c3d4e5f...
> Transaction: 0x...
> Settled: 4.23 HBAR to 5 recipients

Payment Channels

# Open payment channel with peer
nodalync open-channel <peer-id> --deposit 100
> Channel opened: 4d5e6f7a8b9c...
> Peer: ndl1abc123...
> State: Open
> My Balance: 100.00 HBAR
> Their Balance: 100.00 HBAR

# List all payment channels
nodalync list-channels
> Payment Channels: 3 channels (2 open)
>   1a2b3c4d5e6f... ndl1abc... [Open] my: 0.85 HBAR / their: 1.15 HBAR
>   2b3c4d5e6f7a... ndl1def... [Open] my: 2.30 HBAR / their: 0.70 HBAR (5 pending)
>   3c4d5e6f7a8b... ndl1ghi... [Closed] my: 0.00 HBAR / their: 0.00 HBAR

# Close payment channel
nodalync close-channel <peer-id>
> Channel closed: 4d5e6f7a8b9c...
> Peer: ndl1abc123...
> Final Balance: my: 0.85 HBAR / their: 1.15 HBAR

Node Management

# Start node (foreground)
nodalync start
> Starting Nodalync node...
> PeerId: 12D3KooW...
> Listening on /ip4/0.0.0.0/tcp/9000
> Connected to 12 peers
> DHT bootstrapped

# Start with health endpoint (for containers/monitoring)
nodalync start --health --health-port 8080
> Starting Nodalync node...
> PeerId: 12D3KooW...
> Health endpoint: http://0.0.0.0:8080/health
> Metrics endpoint: http://0.0.0.0:8080/metrics

# Start as daemon (background)
nodalync start --daemon
> Nodalync daemon started (PID: 12345)
> PeerId: 12D3KooW...

# Node status
nodalync status
> Node: running (PID: 12345)
> PeerId: 12D3KooW...
> Uptime: 4h 23m
> Peers: 12 connected
> Content: 5 shared, 2 private
> Pending: 12 payments (4.23 HBAR)

# Stop daemon
nodalync stop
> Shutting down gracefully...
> Flushing pending operations...
> Node stopped

Health Endpoints (when --health flag is used):

EndpointContent-TypeDescription
GET /healthapplication/json{"status":"ok","connected_peers":N,"uptime_secs":M}
GET /metricstext/plainPrometheus metrics format

Prometheus Metrics:

  • nodalync_connected_peers — Current peer count
  • nodalync_peer_events_total{event} — Connect/disconnect events
  • nodalync_dht_operations_total{op,result} — DHT put/get operations
  • nodalync_gossipsub_messages_total — Broadcast messages received
  • nodalync_settlement_batches_total{status} — Settlement batches
  • nodalync_settlement_latency_seconds — Settlement operation latency
  • nodalync_queries_total — Total queries processed
  • nodalync_query_latency_seconds — Query latency histogram
  • nodalync_uptime_seconds — Node uptime
  • nodalync_node_info{version,peer_id} — Node metadata

CLI Structure

#![allow(unused)]
fn main() {
use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(name = "nodalync")]
#[command(about = "Nodalync Protocol CLI")]
pub struct Cli {
    #[command(subcommand)]
    pub command: Commands,
    
    /// Path to config file (default: <data_dir>/config.toml)
    #[arg(short, long)]
    pub config: Option<PathBuf>,
    
    /// Output format
    #[arg(short, long, default_value = "human")]
    pub format: OutputFormat,
}

#[derive(Subcommand)]
pub enum Commands {
    /// Initialize new identity
    Init,
    
    /// Show identity info
    Whoami,
    
    /// Publish content
    Publish {
        file: PathBuf,
        #[arg(short, long)]
        price: Option<f64>,
        #[arg(short, long, default_value = "shared")]
        visibility: Visibility,
    },
    
    /// List local content
    List {
        #[arg(short, long)]
        visibility: Option<Visibility>,
    },
    
    /// Search network
    Search {
        query: String,
        #[arg(short, long, default_value = "10")]
        limit: u32,
    },
    
    /// Preview content (free)
    Preview { hash: String },
    
    /// Query content (paid)
    Query { hash: String },
    
    /// Create L3 synthesis
    Synthesize {
        #[arg(short, long, value_delimiter = ',')]
        sources: Vec<String>,
        #[arg(short, long)]
        output: PathBuf,
    },
    
    /// Check balance
    Balance,
    
    /// Start node
    Start {
        #[arg(short, long)]
        daemon: bool,

        /// Enable HTTP health endpoint
        #[arg(long)]
        health: bool,

        /// Port for health endpoint (default: 8080)
        #[arg(long, default_value = "8080")]
        health_port: u16,
    },
    
    /// Node status
    Status,
    
    /// Stop node
    Stop,

    /// Open payment channel
    OpenChannel {
        peer_id: String,
        #[arg(short, long)]
        deposit: f64,
    },

    /// Close payment channel
    CloseChannel { peer_id: String },

    /// List payment channels
    ListChannels,

    // ... more commands
}

#[derive(Clone, Copy, ValueEnum)]
pub enum OutputFormat {
    Human,
    Json,
}
}

Output Formatting

#![allow(unused)]
fn main() {
pub trait Render {
    fn render_human(&self) -> String;
    fn render_json(&self) -> String;
}

impl Render for SearchResult {
    fn render_human(&self) -> String {
        format!(
            "{} \"{}\" by {} ({}/query, {} queries)\n    Preview: {}",
            self.hash.short(),
            self.title,
            self.owner.short(),
            format_amount(self.price),
            self.total_queries,
            self.l1_summary.summary.truncate(80),
        )
    }
    
    fn render_json(&self) -> String {
        serde_json::to_string_pretty(self).unwrap()
    }
}
}

Error Handling

pub fn run() -> Result<()> {
    let cli = Cli::parse();
    
    match cli.command {
        Commands::Publish { file, price, visibility } => {
            let result = publish(&file, price, visibility)?;
            println!("{}", result.render(cli.format));
        }
        // ...
    }
    
    Ok(())
}

fn main() {
    if let Err(e) = run() {
        eprintln!("{}: {}", "Error".red().bold(), e);
        std::process::exit(1);
    }
}

Configuration

Configuration is stored in a platform-specific data directory (set NODALYNC_DATA_DIR to override):

  • macOS: ~/Library/Application Support/io.nodalync.nodalync/config.toml
  • Linux: ~/.local/share/nodalync/config.toml
  • Windows: %APPDATA%\nodalync\nodalync\config.toml
[identity]
keyfile = "<data_dir>/identity/keypair.key"

[storage]
content_dir = "<data_dir>/content"
database = "<data_dir>/nodalync.db"
cache_dir = "<data_dir>/cache"
cache_max_size_mb = 1000

[network]
enabled = true
listen_addresses = ["/ip4/0.0.0.0/tcp/9000"]
bootstrap_nodes = [
    "/dns4/nodalync-bootstrap.eastus.azurecontainer.io/tcp/9000/p2p/12D3KooWMqrUmZm4e1BJTRMWqKHCe1TSX9Vu83uJLEyCGr2dUjYm",
]

[settlement]
network = "hedera-testnet"
auto_deposit = false

[economics]
default_price = 0.1  # In HBAR
auto_settle_threshold = 100.0  # In HBAR

[display]
default_format = "human"
show_previews = true
max_search_results = 20

Test Cases

  1. init: Creates identity and config
  2. publish: File hashed, L1 extracted, announced
  3. search: Returns results from network
  4. query: Pays and retrieves content
  5. synthesize: Creates L3 with correct provenance
  6. balance: Shows correct amounts
  7. JSON output: Valid JSON for all commands
  8. Error messages: Helpful, actionable errors
  9. open-channel: Opens channel, both sides have state
  10. list-channels: Shows all channels with states
  11. close-channel: Cooperative close, settles on-chain