ai-cortex-sdk 0.1.0

Rust client SDK for AI Cortex server: PAT auth, device binding, software store, offline license (Ed25519), and auto-update (check-update pull + SSE push).
Documentation
use ai_cortex_sdk::{
    verify_offline_license, CortexClient, CortexConfig, OfflineLicenseFile,
};

/// 演示离线许可证的拉取 + 本地验签流程:
///   1. 从环境变量读取 server_url / pat / software_id / 可选 pinned pubkey
///   2. 采集当前设备指纹
///   3. fetch_offline_license 拉取许可证文件
///   4. verify_offline_license 本地验签(Ed25519 + 公钥钉扎 + 字段 + 过期)
///   5. 打印 payload / 有效性
///
/// Usage:
///   cargo run --example offline_license -- [PAT] [SOFTWARE_ID] [PINNED_PUBKEY_HEX]
#[tokio::main]
async fn main() {
    let server_url =
        std::env::var("CORTEX_SERVER_URL").unwrap_or_else(|_| "http://localhost:40404".into());
    let pat = std::env::args()
        .nth(1)
        .or_else(|| std::env::var("CORTEX_PAT").ok())
        .unwrap_or_else(|| "REPLACE_WITH_YOUR_PAT".to_string());
    let software_id = std::env::args()
        .nth(2)
        .or_else(|| std::env::var("CORTEX_SOFTWARE_ID").ok())
        .unwrap_or_else(|| "00000000-0000-0000-0000-000000000000".to_string());
    let pinned_pk = std::env::args().nth(3);

    // 关闭心跳,避免示例对服务端产生副作用
    let config = CortexConfig::new(&server_url, &pat, &software_id)
        .without_heartbeat();
    let config = if let Some(pk) = &pinned_pk {
        config.with_software_public_key(pk)
    } else {
        config
    };
    let client = CortexClient::new(config);

    // 采集设备指纹
    let dev = ai_cortex_sdk::device::collect();
    let fingerprint = dev.fingerprint.as_str();
    println!("Device fingerprint: {fingerprint}");

    // 1. 拉取离线许可证
    println!("\n=== Fetch offline license ===");
    let file: OfflineLicenseFile = match client.fetch_offline_license(fingerprint).await {
        Ok(f) => f,
        Err(e) => {
            eprintln!("fetch failed: {e}");
            return;
        }
    };
    println!("public_key: {}", file.public_key);
    println!("signature : {}", file.signature);
    println!("payload   : {}", file.payload);

    // 2. 本地验签(钉扎公钥来自参数)
    println!("\n=== Verify offline license ===");
    match verify_offline_license(
        &file,
        &software_id,
        fingerprint,
        pinned_pk.as_deref(),
    ) {
        Ok(payload) => {
            println!("VALID. payload parsed:");
            println!("  license_id  : {}", payload.license_id);
            println!("  user_id     : {}", payload.user_id);
            println!("  software_id : {}", payload.software_id);
            println!("  fingerprint : {}", payload.fingerprint);
            println!("  max_devices : {}", payload.max_devices);
            println!("  issued_at   : {}", payload.issued_at);
            println!("  expire_time : {}", payload.expire_time);
        }
        Err(e) => {
            eprintln!("INVALID license: {e}");
        }
    }
}