Client Library
6. Client Library
Section titled “6. Client Library”6.1 Public API
Section titled “6.1 Public API”pub struct SyncClient { // Internal: connection, state, crypto}
impl SyncClient { /// Create new client pub async fn new(config: SyncConfig) -> Result<Self>;
/// Connect to relay pub async fn connect(&mut self) -> Result<ConnectionInfo>;
/// Disconnect gracefully pub async fn disconnect(&mut self) -> Result<()>;
/// Push encrypted blob pub async fn push(&self, data: &[u8]) -> Result<PushResult>;
/// Pull blobs after cursor pub async fn pull(&self, after_cursor: u64) -> Result<PullResult>;
/// Subscribe to events pub fn subscribe(&self) -> Receiver<SyncEvent>;
/// Current status pub fn status(&self) -> ConnectionStatus;
/// Last synced cursor (persisted) pub fn last_cursor(&self) -> u64;
/// Device public key pub fn device_id(&self) -> DeviceId;}6.2 Configuration
Section titled “6.2 Configuration”pub struct SyncConfig { /// Relay backend selection pub backend: RelayBackend,
/// Device keypair (generated or loaded) pub device_keypair: Option<Keypair>,
/// Group key (from pairing) pub group_key: Option<GroupKey>,
/// Auto-reconnect on disconnect pub auto_reconnect: bool,
/// Reconnect backoff (initial) pub reconnect_delay: Duration,
/// Max reconnect backoff pub max_reconnect_delay: Duration,}
pub enum RelayBackend { /// Tier 1: iroh public network (P2P + iroh-relay fallback) /// No custom infrastructure needed. Iroh,
/// Tiers 2-3: Self-hosted sync-relay (iroh Endpoint) /// Connect to a known sync-relay by NodeId. SyncRelay { node_id: iroh::NodeId, relay_url: Option<Url>, // Optional custom iroh-relay for this deployment },
/// Tiers 4-5: Managed Cloud /// API key resolves to a sync-relay NodeId via discovery service. ManagedCloud { api_key: String, },
/// Tier 6: Enterprise /// Dedicated sync-relay with enterprise authentication. Enterprise { node_id: iroh::NodeId, auth: EnterpriseAuth, },}6.3 Events
Section titled “6.3 Events”pub enum SyncEvent { /// Connected to relay Connected { info: ConnectionInfo },
/// Disconnected from relay Disconnected { reason: String },
/// New blob available (NOTIFY received) BlobAvailable { id: BlobId, cursor: u64, sender: DeviceId },
/// Blob successfully pushed BlobPushed { id: BlobId, cursor: u64 },
/// Error occurred Error { code: u32, message: String },
/// Connection status changed StatusChanged { status: ConnectionStatus },}
pub enum ConnectionStatus { Disconnected, Connecting, Connected, Reconnecting { attempt: u32 },}6.4 Encryption Flow
Section titled “6.4 Encryption Flow”Encrypt (before push):
plaintext → serialize (MessagePack) → encrypt (XChaCha20-Poly1305 with Group Key + random 192-bit nonce) → wrap in Envelope → send via iroh (QUIC/TLS 1.3)Decrypt (after pull):
receive via iroh (QUIC/TLS 1.3) → unwrap Envelope → decrypt (XChaCha20-Poly1305 with Group Key + nonce from envelope) → deserialize (MessagePack) → plaintextWhy XChaCha20 (not standard ChaCha20)? Standard ChaCha20-Poly1305 uses 96-bit nonces with a safe threshold of ~4.3 billion messages. XChaCha20 uses 192-bit nonces, making random nonce generation safe without cross-device coordination (safe threshold: 2^80).