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
- Fetch and cache the root-signed attesters bundle (e.g., from Ops) and build a trust view.
- Use
DiscoveryClientto broadcast a request and inspect the returnedDiscoveryOutcomefor identity, capability, server nonce, and trusted identity status. - Call
AlpineClient::connectwith the discovered identity, capability set, and a credential pair; the SDK spins up the transport plus the keep-alive task. - Call
AlpineClient::start_stream, pass aStreamProfile, and track the returnedconfig_id. - Use
send_frameto push encodedFrameEnvelopes orsend_controlfor control envelopes. - Call
AlpineClient::ping,status,health,identity, ormetadatato 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.