pub struct CortexConfig {
pub server_url: String,
pub pat: String,
pub software_id: String,
pub timeout: u64,
pub heartbeat_interval: u64,
pub software_public_key: Option<String>,
}Expand description
SDK 配置。
鉴权改为用户 PAT(Authorization: Bearer actx_pat_...),不再使用 app_key/app_secret HMAC。
PAT 由用户在 web 端创建后填入配置。
Fields§
§server_url: String§pat: String用户个人访问令牌(PAT),形如 actx_pat_...。
software_id: StringSDK 以哪个软件身份心跳/下载(设备按该软件的许可证 max_devices 校验上限)。
timeout: u64§heartbeat_interval: u64心跳上报间隔(秒)。0 = 关闭心跳;默认 60s。
software_public_key: Option<String>可选:钉扎的软件公钥(hex)。设置后,拉取到的离线许可证 public_key 必须与之匹配才信任。 不设置则信任服务端在 issue 响应中下发的 public_key(依赖 TLS)。
Implementations§
Source§impl CortexConfig
impl CortexConfig
Sourcepub fn new(
server_url: impl Into<String>,
pat: impl Into<String>,
software_id: impl Into<String>,
) -> Self
pub fn new( server_url: impl Into<String>, pat: impl Into<String>, software_id: impl Into<String>, ) -> Self
Examples found in repository?
examples/auth_check.rs (line 16)
8async fn main() {
9 let pat = std::env::args()
10 .nth(1)
11 .unwrap_or_else(|| "REPLACE_WITH_YOUR_PAT".to_string());
12
13 // software_id 占位:心跳会按此软件的许可证校验设备上限。
14 let software_id = "00000000-0000-0000-0000-000000000000".to_string();
15
16 let config = CortexConfig::new("http://localhost:40404", pat, software_id);
17 let client = CortexClient::new(config);
18
19 match client.list_software().await {
20 Ok(items) => {
21 println!("PAT valid. Software store returned {} item(s).", items.len());
22 }
23 Err(e) => {
24 eprintln!("PAT check failed: {e}");
25 std::process::exit(1);
26 }
27 }
28
29 println!("\n--- My Devices ---");
30 match client.list_devices().await {
31 Ok(devices) => {
32 println!("{} bound device(s).", devices.len());
33 for d in devices {
34 println!(
35 "- fingerprint={}, last_active={}",
36 d.fingerprint, d.last_active_time
37 );
38 }
39 }
40 Err(e) => eprintln!("List devices failed: {e}"),
41 }
42}More examples
examples/offline_license.rs (line 29)
15async fn main() {
16 let server_url =
17 std::env::var("CORTEX_SERVER_URL").unwrap_or_else(|_| "http://localhost:40404".into());
18 let pat = std::env::args()
19 .nth(1)
20 .or_else(|| std::env::var("CORTEX_PAT").ok())
21 .unwrap_or_else(|| "REPLACE_WITH_YOUR_PAT".to_string());
22 let software_id = std::env::args()
23 .nth(2)
24 .or_else(|| std::env::var("CORTEX_SOFTWARE_ID").ok())
25 .unwrap_or_else(|| "00000000-0000-0000-0000-000000000000".to_string());
26 let pinned_pk = std::env::args().nth(3);
27
28 // 关闭心跳,避免示例对服务端产生副作用
29 let config = CortexConfig::new(&server_url, &pat, &software_id)
30 .without_heartbeat();
31 let config = if let Some(pk) = &pinned_pk {
32 config.with_software_public_key(pk)
33 } else {
34 config
35 };
36 let client = CortexClient::new(config);
37
38 // 采集设备指纹
39 let dev = ai_cortex_sdk::device::collect();
40 let fingerprint = dev.fingerprint.as_str();
41 println!("Device fingerprint: {fingerprint}");
42
43 // 1. 拉取离线许可证
44 println!("\n=== Fetch offline license ===");
45 let file: OfflineLicenseFile = match client.fetch_offline_license(fingerprint).await {
46 Ok(f) => f,
47 Err(e) => {
48 eprintln!("fetch failed: {e}");
49 return;
50 }
51 };
52 println!("public_key: {}", file.public_key);
53 println!("signature : {}", file.signature);
54 println!("payload : {}", file.payload);
55
56 // 2. 本地验签(钉扎公钥来自参数)
57 println!("\n=== Verify offline license ===");
58 match verify_offline_license(
59 &file,
60 &software_id,
61 fingerprint,
62 pinned_pk.as_deref(),
63 ) {
64 Ok(payload) => {
65 println!("VALID. payload parsed:");
66 println!(" license_id : {}", payload.license_id);
67 println!(" user_id : {}", payload.user_id);
68 println!(" software_id : {}", payload.software_id);
69 println!(" fingerprint : {}", payload.fingerprint);
70 println!(" max_devices : {}", payload.max_devices);
71 println!(" issued_at : {}", payload.issued_at);
72 println!(" expire_time : {}", payload.expire_time);
73 }
74 Err(e) => {
75 eprintln!("INVALID license: {e}");
76 }
77 }
78}examples/basic.rs (line 16)
7async fn main() {
8 let server_url = "http://localhost:40404";
9 let pat = std::env::args()
10 .nth(1)
11 .unwrap_or_else(|| "REPLACE_WITH_YOUR_PAT".to_string());
12
13 // software_id:SDK 以哪个软件身份心跳/下载,需与用户持有的有效 auth_record.software_id 一致。
14 let software_id = "00000000-0000-0000-0000-000000000000".to_string();
15
16 let config = CortexConfig::new(server_url, pat, software_id).with_timeout(30);
17 let client = CortexClient::new(config);
18
19 // --- List Software ---
20 println!("=== Software Store ===");
21 match client.list_software().await {
22 Ok(items) => {
23 if items.is_empty() {
24 println!("No software available.");
25 }
26 for (i, item) in items.iter().enumerate() {
27 let version_str = item
28 .latest_version
29 .as_ref()
30 .map(|v| format!("v{} ({} bytes)", v.version, v.file_size))
31 .unwrap_or_else(|| "no version".to_string());
32 println!(
33 "{}. {} — {} [{}]",
34 i + 1,
35 item.software.name,
36 item.software.description,
37 version_str
38 );
39 }
40
41 // --- Download first software's latest version ---
42 if let Some(item) = items.first() {
43 if let Some(version) = &item.latest_version {
44 println!("\n=== Download ===");
45 match client.download(&version.id).await {
46 Ok(info) => {
47 println!(
48 "Download: v{}, size={} bytes\n URL: {}",
49 info.version, info.file_size, info.download_url
50 );
51 }
52 Err(e) => eprintln!("Download failed: {e}"),
53 }
54 }
55 }
56 }
57 Err(e) => eprintln!("List software failed: {e}"),
58 }
59
60 // --- List Devices (PAT 所属用户的设备;心跳在后台自动绑定) ---
61 println!("\n=== My Devices ===");
62 match client.list_devices().await {
63 Ok(devices) => {
64 if devices.is_empty() {
65 println!("No bound devices yet (heartbeat may not have fired yet).");
66 }
67 for d in devices {
68 println!(
69 "- id={}, fingerprint={}, last_active={}",
70 d.id, d.fingerprint, d.last_active_time
71 );
72 }
73 }
74 Err(e) => eprintln!("List devices failed: {e}"),
75 }
76}examples/auto_update.rs (line 28)
13async fn main() {
14 let server_url =
15 std::env::var("CORTEX_SERVER_URL").unwrap_or_else(|_| "http://localhost:40404".into());
16 let pat = std::env::args()
17 .nth(1)
18 .or_else(|| std::env::var("CORTEX_PAT").ok())
19 .unwrap_or_else(|| "REPLACE_WITH_YOUR_PAT".to_string());
20 let software_id = std::env::args()
21 .nth(2)
22 .or_else(|| std::env::var("CORTEX_SOFTWARE_ID").ok())
23 .unwrap_or_else(|| "00000000-0000-0000-0000-000000000000".to_string());
24 // 第 3 个位置参数(任意值)= 演示 SSE 流
25 let demo_stream = std::env::args().nth(3).is_some();
26
27 // 关闭心跳,避免示例对服务端产生副作用
28 let config = CortexConfig::new(&server_url, &pat, &software_id).without_heartbeat();
29 let client = CortexClient::new(config);
30
31 // --- 1. Pull:检查更新 ---
32 println!("=== Check Update (Pull) ===");
33 let platform = std::env::consts::OS; // linux / macos / windows
34 let current_version = "1.0.0";
35 match client
36 .check_update(platform, current_version, None)
37 .await
38 {
39 Ok(info) => {
40 println!("current_version : {}", info.current_version);
41 println!("has_update : {}", info.has_update);
42 println!("force_update : {}", info.force_update);
43 if let Some(t) = &info.target_version {
44 println!("target_version : {} (channel={}, size={} bytes)", t.version, t.channel, t.file_size);
45 println!("release_notes : {}", t.release_notes);
46 println!("min_compatible : {}", t.min_compatible);
47 }
48 // force_update 语义:客户端应阻止启动直到用户升级
49 if info.has_update && info.force_update {
50 println!("\n>>> 强制更新:当前版本被阻塞,必须升级后才能继续。");
51 }
52 }
53 Err(e) => eprintln!("check_update failed: {e}"),
54 }
55
56 // --- 2. Push:订阅 SSE 更新事件流(可选) ---
57 if demo_stream {
58 println!("\n=== Update Events (Push / SSE) ===");
59 match client.open_update_events(Some(&software_id), None).await {
60 Ok(mut stream) => {
61 println!("SSE connected. Waiting for events (will print 3 then exit)...");
62 let mut count = 0usize;
63 while let Some(ev) = stream.next().await {
64 match ev {
65 Ok(e) => {
66 println!(
67 "event #{}: software={} version={} platform={} channel={} force={}",
68 e.id, e.software_id, e.version, e.platform, e.channel, e.force_update
69 );
70 count += 1;
71 if count >= 3 {
72 println!("Received 3 events, exiting demo.");
73 break;
74 }
75 }
76 Err(e) => {
77 eprintln!("stream error: {e}");
78 break;
79 }
80 }
81 }
82 }
83 Err(e) => eprintln!("open_update_events failed: {e}"),
84 }
85 }
86}Sourcepub fn with_timeout(self, timeout: u64) -> Self
pub fn with_timeout(self, timeout: u64) -> Self
Examples found in repository?
examples/basic.rs (line 16)
7async fn main() {
8 let server_url = "http://localhost:40404";
9 let pat = std::env::args()
10 .nth(1)
11 .unwrap_or_else(|| "REPLACE_WITH_YOUR_PAT".to_string());
12
13 // software_id:SDK 以哪个软件身份心跳/下载,需与用户持有的有效 auth_record.software_id 一致。
14 let software_id = "00000000-0000-0000-0000-000000000000".to_string();
15
16 let config = CortexConfig::new(server_url, pat, software_id).with_timeout(30);
17 let client = CortexClient::new(config);
18
19 // --- List Software ---
20 println!("=== Software Store ===");
21 match client.list_software().await {
22 Ok(items) => {
23 if items.is_empty() {
24 println!("No software available.");
25 }
26 for (i, item) in items.iter().enumerate() {
27 let version_str = item
28 .latest_version
29 .as_ref()
30 .map(|v| format!("v{} ({} bytes)", v.version, v.file_size))
31 .unwrap_or_else(|| "no version".to_string());
32 println!(
33 "{}. {} — {} [{}]",
34 i + 1,
35 item.software.name,
36 item.software.description,
37 version_str
38 );
39 }
40
41 // --- Download first software's latest version ---
42 if let Some(item) = items.first() {
43 if let Some(version) = &item.latest_version {
44 println!("\n=== Download ===");
45 match client.download(&version.id).await {
46 Ok(info) => {
47 println!(
48 "Download: v{}, size={} bytes\n URL: {}",
49 info.version, info.file_size, info.download_url
50 );
51 }
52 Err(e) => eprintln!("Download failed: {e}"),
53 }
54 }
55 }
56 }
57 Err(e) => eprintln!("List software failed: {e}"),
58 }
59
60 // --- List Devices (PAT 所属用户的设备;心跳在后台自动绑定) ---
61 println!("\n=== My Devices ===");
62 match client.list_devices().await {
63 Ok(devices) => {
64 if devices.is_empty() {
65 println!("No bound devices yet (heartbeat may not have fired yet).");
66 }
67 for d in devices {
68 println!(
69 "- id={}, fingerprint={}, last_active={}",
70 d.id, d.fingerprint, d.last_active_time
71 );
72 }
73 }
74 Err(e) => eprintln!("List devices failed: {e}"),
75 }
76}Sourcepub fn with_software_public_key(self, pk: impl Into<String>) -> Self
pub fn with_software_public_key(self, pk: impl Into<String>) -> Self
钉扎软件公钥(hex)。设置后离线许可证验签时要求下发公钥与之严格匹配。
Examples found in repository?
examples/offline_license.rs (line 32)
15async fn main() {
16 let server_url =
17 std::env::var("CORTEX_SERVER_URL").unwrap_or_else(|_| "http://localhost:40404".into());
18 let pat = std::env::args()
19 .nth(1)
20 .or_else(|| std::env::var("CORTEX_PAT").ok())
21 .unwrap_or_else(|| "REPLACE_WITH_YOUR_PAT".to_string());
22 let software_id = std::env::args()
23 .nth(2)
24 .or_else(|| std::env::var("CORTEX_SOFTWARE_ID").ok())
25 .unwrap_or_else(|| "00000000-0000-0000-0000-000000000000".to_string());
26 let pinned_pk = std::env::args().nth(3);
27
28 // 关闭心跳,避免示例对服务端产生副作用
29 let config = CortexConfig::new(&server_url, &pat, &software_id)
30 .without_heartbeat();
31 let config = if let Some(pk) = &pinned_pk {
32 config.with_software_public_key(pk)
33 } else {
34 config
35 };
36 let client = CortexClient::new(config);
37
38 // 采集设备指纹
39 let dev = ai_cortex_sdk::device::collect();
40 let fingerprint = dev.fingerprint.as_str();
41 println!("Device fingerprint: {fingerprint}");
42
43 // 1. 拉取离线许可证
44 println!("\n=== Fetch offline license ===");
45 let file: OfflineLicenseFile = match client.fetch_offline_license(fingerprint).await {
46 Ok(f) => f,
47 Err(e) => {
48 eprintln!("fetch failed: {e}");
49 return;
50 }
51 };
52 println!("public_key: {}", file.public_key);
53 println!("signature : {}", file.signature);
54 println!("payload : {}", file.payload);
55
56 // 2. 本地验签(钉扎公钥来自参数)
57 println!("\n=== Verify offline license ===");
58 match verify_offline_license(
59 &file,
60 &software_id,
61 fingerprint,
62 pinned_pk.as_deref(),
63 ) {
64 Ok(payload) => {
65 println!("VALID. payload parsed:");
66 println!(" license_id : {}", payload.license_id);
67 println!(" user_id : {}", payload.user_id);
68 println!(" software_id : {}", payload.software_id);
69 println!(" fingerprint : {}", payload.fingerprint);
70 println!(" max_devices : {}", payload.max_devices);
71 println!(" issued_at : {}", payload.issued_at);
72 println!(" expire_time : {}", payload.expire_time);
73 }
74 Err(e) => {
75 eprintln!("INVALID license: {e}");
76 }
77 }
78}Sourcepub fn with_software_id(self, software_id: impl Into<String>) -> Self
pub fn with_software_id(self, software_id: impl Into<String>) -> Self
设置 SDK 心跳/下载所代表的软件 id。
Sourcepub fn with_heartbeat_interval(self, secs: u64) -> Self
pub fn with_heartbeat_interval(self, secs: u64) -> Self
设置心跳上报间隔(秒)。设为 0 可关闭心跳。
Sourcepub fn without_heartbeat(self) -> Self
pub fn without_heartbeat(self) -> Self
关闭心跳上报。
Examples found in repository?
examples/offline_license.rs (line 30)
15async fn main() {
16 let server_url =
17 std::env::var("CORTEX_SERVER_URL").unwrap_or_else(|_| "http://localhost:40404".into());
18 let pat = std::env::args()
19 .nth(1)
20 .or_else(|| std::env::var("CORTEX_PAT").ok())
21 .unwrap_or_else(|| "REPLACE_WITH_YOUR_PAT".to_string());
22 let software_id = std::env::args()
23 .nth(2)
24 .or_else(|| std::env::var("CORTEX_SOFTWARE_ID").ok())
25 .unwrap_or_else(|| "00000000-0000-0000-0000-000000000000".to_string());
26 let pinned_pk = std::env::args().nth(3);
27
28 // 关闭心跳,避免示例对服务端产生副作用
29 let config = CortexConfig::new(&server_url, &pat, &software_id)
30 .without_heartbeat();
31 let config = if let Some(pk) = &pinned_pk {
32 config.with_software_public_key(pk)
33 } else {
34 config
35 };
36 let client = CortexClient::new(config);
37
38 // 采集设备指纹
39 let dev = ai_cortex_sdk::device::collect();
40 let fingerprint = dev.fingerprint.as_str();
41 println!("Device fingerprint: {fingerprint}");
42
43 // 1. 拉取离线许可证
44 println!("\n=== Fetch offline license ===");
45 let file: OfflineLicenseFile = match client.fetch_offline_license(fingerprint).await {
46 Ok(f) => f,
47 Err(e) => {
48 eprintln!("fetch failed: {e}");
49 return;
50 }
51 };
52 println!("public_key: {}", file.public_key);
53 println!("signature : {}", file.signature);
54 println!("payload : {}", file.payload);
55
56 // 2. 本地验签(钉扎公钥来自参数)
57 println!("\n=== Verify offline license ===");
58 match verify_offline_license(
59 &file,
60 &software_id,
61 fingerprint,
62 pinned_pk.as_deref(),
63 ) {
64 Ok(payload) => {
65 println!("VALID. payload parsed:");
66 println!(" license_id : {}", payload.license_id);
67 println!(" user_id : {}", payload.user_id);
68 println!(" software_id : {}", payload.software_id);
69 println!(" fingerprint : {}", payload.fingerprint);
70 println!(" max_devices : {}", payload.max_devices);
71 println!(" issued_at : {}", payload.issued_at);
72 println!(" expire_time : {}", payload.expire_time);
73 }
74 Err(e) => {
75 eprintln!("INVALID license: {e}");
76 }
77 }
78}More examples
examples/auto_update.rs (line 28)
13async fn main() {
14 let server_url =
15 std::env::var("CORTEX_SERVER_URL").unwrap_or_else(|_| "http://localhost:40404".into());
16 let pat = std::env::args()
17 .nth(1)
18 .or_else(|| std::env::var("CORTEX_PAT").ok())
19 .unwrap_or_else(|| "REPLACE_WITH_YOUR_PAT".to_string());
20 let software_id = std::env::args()
21 .nth(2)
22 .or_else(|| std::env::var("CORTEX_SOFTWARE_ID").ok())
23 .unwrap_or_else(|| "00000000-0000-0000-0000-000000000000".to_string());
24 // 第 3 个位置参数(任意值)= 演示 SSE 流
25 let demo_stream = std::env::args().nth(3).is_some();
26
27 // 关闭心跳,避免示例对服务端产生副作用
28 let config = CortexConfig::new(&server_url, &pat, &software_id).without_heartbeat();
29 let client = CortexClient::new(config);
30
31 // --- 1. Pull:检查更新 ---
32 println!("=== Check Update (Pull) ===");
33 let platform = std::env::consts::OS; // linux / macos / windows
34 let current_version = "1.0.0";
35 match client
36 .check_update(platform, current_version, None)
37 .await
38 {
39 Ok(info) => {
40 println!("current_version : {}", info.current_version);
41 println!("has_update : {}", info.has_update);
42 println!("force_update : {}", info.force_update);
43 if let Some(t) = &info.target_version {
44 println!("target_version : {} (channel={}, size={} bytes)", t.version, t.channel, t.file_size);
45 println!("release_notes : {}", t.release_notes);
46 println!("min_compatible : {}", t.min_compatible);
47 }
48 // force_update 语义:客户端应阻止启动直到用户升级
49 if info.has_update && info.force_update {
50 println!("\n>>> 强制更新:当前版本被阻塞,必须升级后才能继续。");
51 }
52 }
53 Err(e) => eprintln!("check_update failed: {e}"),
54 }
55
56 // --- 2. Push:订阅 SSE 更新事件流(可选) ---
57 if demo_stream {
58 println!("\n=== Update Events (Push / SSE) ===");
59 match client.open_update_events(Some(&software_id), None).await {
60 Ok(mut stream) => {
61 println!("SSE connected. Waiting for events (will print 3 then exit)...");
62 let mut count = 0usize;
63 while let Some(ev) = stream.next().await {
64 match ev {
65 Ok(e) => {
66 println!(
67 "event #{}: software={} version={} platform={} channel={} force={}",
68 e.id, e.software_id, e.version, e.platform, e.channel, e.force_update
69 );
70 count += 1;
71 if count >= 3 {
72 println!("Received 3 events, exiting demo.");
73 break;
74 }
75 }
76 Err(e) => {
77 eprintln!("stream error: {e}");
78 break;
79 }
80 }
81 }
82 }
83 Err(e) => eprintln!("open_update_events failed: {e}"),
84 }
85 }
86}Trait Implementations§
Source§impl Clone for CortexConfig
impl Clone for CortexConfig
Source§fn clone(&self) -> CortexConfig
fn clone(&self) -> CortexConfig
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for CortexConfig
impl Debug for CortexConfig
Source§impl<'de> Deserialize<'de> for CortexConfig
impl<'de> Deserialize<'de> for CortexConfig
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Deserialize this value from the given Serde deserializer. Read more
Auto Trait Implementations§
impl Freeze for CortexConfig
impl RefUnwindSafe for CortexConfig
impl Send for CortexConfig
impl Sync for CortexConfig
impl Unpin for CortexConfig
impl UnsafeUnpin for CortexConfig
impl UnwindSafe for CortexConfig
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more