nomadnet-rs 0.3.1

Rust library for NomadNet Node Hosting and browsing over Reticulum
pub const DEFAULT_ANNOUNCE_INTERVAL_SECS: u64 = 600;
pub const MAX_DIRECTORY_ENTRIES: usize = 256;
pub const ANNOUNCE_STREAM_MAXLENGTH: usize = 256;

/// Configuration for creating a [`NomadNode`](crate::NomadNode).
pub struct NodeConfig {
    pub identity_prv: [u8; 64],
    pub identity_pub: [u8; 32],
    pub node_name: String,
    pub announce_interval_secs: u64,
}

impl Default for NodeConfig {
    fn default() -> Self {
        Self {
            identity_prv: [0u8; 64],
            identity_pub: [0u8; 32],
            node_name: String::from("Unnamed Node"),
            announce_interval_secs: DEFAULT_ANNOUNCE_INTERVAL_SECS,
        }
    }
}

/// A discovered NomadNet node in the directory.
pub struct DirectoryEntry {
    pub dest_hash: [u8; 16],
    pub identity_hash: [u8; 16],
    pub node_name: Option<String>,
    pub hops: u8,
    pub last_seen: f64,
    pub trust_level: TrustLevel,
}

/// Trust level for a known peer.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum TrustLevel {
    Warning = 0x00,
    Untrusted = 0x01,
    #[default]
    Unknown = 0x02,
    Trusted = 0xFF,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum DeliveryMethod {
    #[default]
    Direct = 0x01,
    Propagated = 0x02,
}

/// A manually remembered peer in the directory.
pub struct PeerEntry {
    pub source_hash: [u8; 16],
    pub display_name: Option<String>,
    pub trust_level: TrustLevel,
    pub hosts_node: bool,
    pub preferred_delivery: DeliveryMethod,
    pub identify_on_connect: bool,
    pub sort_rank: Option<i32>,
    pub notes: String,
    pub identity_hash: Option<[u8; 16]>,
}

/// The kind of announce in the stream.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AnnounceKind {
    Node,
    Peer,
    Pn,
}

/// An entry in the announce stream.
#[derive(Debug, Clone)]
pub struct AnnounceStreamEntry {
    pub timestamp: f64,
    pub source_hash: [u8; 16],
    pub app_data: Option<Vec<u8>>,
    pub kind: AnnounceKind,
}

/// Events emitted by [`NomadBrowser`](crate::NomadBrowser).
#[derive(Debug)]
pub enum BrowseEvent {
    PageReceived {
        dest_hash: [u8; 16],
        path: String,
        content: Vec<u8>,
    },
    FileReceived {
        dest_hash: [u8; 16],
        path: String,
        content: Vec<u8>,
        file_name: Option<String>,
    },
    LinkEstablished {
        dest_hash: [u8; 16],
        link_id: [u8; 16],
    },
    LinkFailed {
        dest_hash: [u8; 16],
        error: String,
    },
    LinkClosed {
        dest_hash: [u8; 16],
        link_id: [u8; 16],
        reason: Option<String>,
    },
    RrcLinkReceived {
        hub_hash: [u8; 16],
        dest_name: Option<String>,
        room: Option<String>,
    },
    LxmfLinkReceived {
        dest_hash: [u8; 16],
    },
    PageCached {
        url: String,
    },
    CacheMissed {
        url: String,
    },
    HistoryNavigated {
        dest_hash: [u8; 16],
        path: String,
    },
    PartialReceived {
        partial_hash: String,
        content: Vec<u8>,
    },
    PartialFailed {
        partial_hash: String,
        error: String,
    },
    TransferProgress {
        dest_hash: [u8; 16],
        path: String,
        progress: f64,
        data_size: Option<u64>,
        transfer_size: Option<u64>,
        speed_bps: Option<f64>,
    },
}

/// Default page cache time in seconds (12 hours).
pub const DEFAULT_CACHE_TIME_SECS: u64 = 12 * 60 * 60;

/// Shorthand mappings for NomadNet URL type prefixes.
pub const SHORTHAND_NNN: &str = "nnn";
pub const SHORTHAND_LXMF: &str = "lxmf";
pub const SHORTHAND_RRC: &str = "rrc";
pub const DEST_TYPE_NOMADNET_NODE: &str = "nomadnetwork.node";
pub const DEST_TYPE_LXMF_DELIVERY: &str = "lxmf.delivery";
pub const DEST_TYPE_RRC_HUB: &str = "rrc.hub.session";

#[derive(Debug, thiserror::Error)]
pub enum NomadError {
    #[error("RNS send error")]
    SendFailed(#[from] rns_net::SendError),

    #[error("Request data encoding failed")]
    RequestDataEncoding(#[from] rmp_serde::encode::Error),

    #[error("No path to destination {0}")]
    NoPath(String),

    #[error("Identity not found for destination {0}")]
    IdentityNotFound(String),

    #[error("Link creation failed for destination {0}")]
    LinkFailed(String),

    #[error("Request failed for path {0}")]
    RequestFailed(String),

    #[error("Destination registration failed")]
    DestinationRegistrationFailed,

    #[error(
        "Identity public key mismatch: expected {expected_sig_pub_hex}, got {provided_sig_pub_hex}"
    )]
    IdentityKeyMismatch {
        expected_sig_pub_hex: String,
        provided_sig_pub_hex: String,
    },
}