alpine-protocol-sdk 0.2.2

High-level SDK on top of the ALPINE protocol layer.
Documentation

ALPINE Rust SDK

alpine-protocol-sdk is a high-level wrapper around the published alpine-protocol-rs protocol artifacts. It keeps discovery, handshake, and streaming lifecycles explicit so application code can reason about each step without diving into the lower-level protocol helpers.

When to use the SDK vs the protocol layer

  • Protocol layer (alpine-protocol-rs) gives you access to every message, frame, and handshake primitive and is useful if you already have a transport layer or want to implement a custom state machine.
  • SDK (alpine-protocol-sdk) builds on top of the protocol layer and manages sockets, keep-alive, and profile lifetimes so you can focus on discovery → connect → streaming flows.

Quick lifecycle

  1. Fetch and cache the root-signed attesters bundle (e.g., from Ops) and build a trust view.
  2. Use DiscoveryClient to broadcast a request and inspect the returned DiscoveryOutcome for identity, capability, server nonce, and trusted identity status.
  3. Call AlpineClient::connect with the discovered identity, capability set, and a credential pair; the SDK spins up the transport plus the keep-alive task.
  4. Call AlpineClient::start_stream, pass a StreamProfile, and track the returned config_id.
  5. Use send_frame to push encoded FrameEnvelopes or send_control for control envelopes.
  6. Call AlpineClient::ping, status, health, identity, or metadata to send the corresponding control command and receive typed replies when the device returns structured CBOR payloads.

Example

use alpine_protocol_sdk::{AlpineClient, DiscoveryClient, DiscoveryClientOptions};
use alpine_protocol_rs::{
    crypto::identity::NodeCredentials,
    messages::{CapabilitySet, DeviceIdentity},
    profile::StreamProfile,
};
use std::net::{IpAddr, SocketAddr};

#[tokio::main]
async fn main() -> Result<(), alpine_protocol_sdk::AlpineSdkError> {
    let discovery = DiscoveryClient::new(DiscoveryClientOptions::new(
        SocketAddr::new(IpAddr::V4([0, 0, 0, 0].into()), 0),
        SocketAddr::new(IpAddr::V4([192, 168, 1, 42].into()), 5555),
        std::time::Duration::from_secs(3),
    ))?;
    let outcome = discovery.discover(&["alpine-control".to_string()])?;
    let remote = SocketAddr::new(outcome.peer.ip(), outcome.peer.port());
    let identity = DeviceIdentity {
        device_id: outcome.reply.device_id.clone(),
        manufacturer_id: outcome.reply.manufacturer_id.clone(),
        model_id: outcome.reply.model_id.clone(),
        hardware_rev: outcome.reply.hardware_rev.clone(),
        firmware_rev: outcome.reply.firmware_rev.clone(),
    };
    let credentials = NodeCredentials::load("path/to/credentials")?;
    let capabilities = outcome.reply.capabilities.clone();

    let mut client = AlpineClient::connect(
        SocketAddr::new(IpAddr::V4([0, 0, 0, 0].into()), 0),
        remote,
        identity,
        capabilities,
        credentials,
    )
    .await?;
    let config_id = client.start_stream(StreamProfile::auto())?;
    println!("Streaming with config id {}", config_id);
    Ok(())
}

Every exported module in this crate has /// documentation so the generated docs on docs.rs describe the same lifecycle described here.