PlatformsRust
Rust
Implement the Aelio SDK in Rust using the wire protocol — community guide.
Status: No official Rust SDK yet. Rust in the Aelio ecosystem is the Sunjet/Astrolobe storage engine, not a Convox SDK target. This guide shows how to implement the wire protocol in Rust for backends built with Actix, Axum, or Tokio.
Dependencies
# Cargo.toml
[dependencies]
tokio = { version = "1", features = ["full"] }
tokio-tungstenite = { version = "0.21", features = ["native-tls"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
futures-util = "0.3"Step 1: Types
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize)]
struct RegisterMessage {
#[serde(rename = "type")]
msg_type: String,
functions: Vec<FunctionDef>,
language: String,
sdkVersion: String,
}
#[derive(Serialize)]
struct FunctionDef {
name: String,
description: String,
params: HashMap<String, serde_json::Value>,
safety: String,
}
#[derive(Deserialize)]
struct IncomingMessage {
#[serde(rename = "type")]
msg_type: String,
id: Option<String>,
function: Option<String>,
args: Option<HashMap<String, serde_json::Value>>,
context: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Serialize)]
struct ResultMessage {
#[serde(rename = "type")]
msg_type: String,
id: String,
ok: bool,
#[serde(skip_serializing_if = "Option::is_none")]
data: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<ErrorBody>,
}
#[derive(Serialize)]
struct ErrorBody {
code: String,
message: String,
retryable: bool,
}
type Handler = Box<dyn Fn(HashMap<String, serde_json::Value>, HashMap<String, serde_json::Value>)
-> Result<serde_json::Value, String>
+ Send
+ Sync>;Step 2: SDK client
use tokio_tungstenite::{connect_async, tungstenite::client::IntoClientRequest};
use futures_util::{StreamExt, SinkExt};
pub struct AelioSDK {
secret: String,
url: String,
handlers: HashMap<String, Handler>,
functions: Vec<FunctionDef>,
}
impl AelioSDK {
pub fn new(secret: &str, url: &str) -> Self {
Self {
secret: secret.to_string(),
url: format!("{}/sdk", url.trim_end_matches('/')),
handlers: HashMap::new(),
functions: Vec::new(),
}
}
pub fn expose<F>(&mut self, name: &str, description: &str, safety: &str, handler: F)
where
F: Fn(HashMap<String, serde_json::Value>, HashMap<String, serde_json::Value>)
-> Result<serde_json::Value, String>
+ Send
+ Sync
+ 'static,
{
self.handlers.insert(name.to_string(), Box::new(handler));
self.functions.push(FunctionDef {
name: name.to_string(),
description: description.to_string(),
params: HashMap::from([(
"orderId".to_string(),
serde_json::json!({"type": "string"}),
)]),
safety: safety.to_string(),
});
}
pub async fn listen(&self) -> Result<(), Box<dyn std::error::Error>> {
let mut request = self.url.as_str().into_client_request()?;
request.headers_mut().insert(
"Authorization",
format!("Bearer {}", self.secret).parse()?,
);
let (ws, _) = connect_async(request).await?;
let (mut write, mut read) = ws.split();
// Register
let register = RegisterMessage {
msg_type: "register".to_string(),
functions: self.functions.clone(),
language: "rust".to_string(),
sdkVersion: "0.1.0".to_string(),
};
write.send(serde_json::to_string(®ister)?.into()).await?;
println!("Aelio SDK connected (Rust)");
// Listen
while let Some(msg) = read.next().await {
let text = msg?.into_text()?;
let incoming: IncomingMessage = serde_json::from_str(&text)?;
match incoming.msg_type.as_str() {
"invoke" => {
let id = incoming.id.unwrap_or_default();
let function = incoming.function.unwrap_or_default();
let args = incoming.args.unwrap_or_default();
let ctx = incoming.context.unwrap_or_default();
let result = if let Some(handler) = self.handlers.get(&function) {
match handler(args, ctx) {
Ok(data) => ResultMessage {
msg_type: "result".to_string(),
id: id.clone(),
ok: true,
data: Some(data),
error: None,
},
Err(e) => ResultMessage {
msg_type: "result".to_string(),
id: id.clone(),
ok: false,
data: None,
error: Some(ErrorBody {
code: "HANDLER_ERROR".to_string(),
message: e,
retryable: false,
}),
},
}
} else {
ResultMessage {
msg_type: "result".to_string(),
id: id.clone(),
ok: false,
data: None,
error: Some(ErrorBody {
code: "FUNCTION_NOT_FOUND".to_string(),
message: function,
retryable: false,
}),
}
};
write.send(serde_json::to_string(&result)?.into()).await?;
}
"ping" => {
write.send(r#"{"type":"pong"}"#.into()).await?;
}
_ => {}
}
}
Ok(())
}
}Step 3: Usage
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut aelio = AelioSDK::new(
&std::env::var("AELIO_SDK_SECRET").unwrap_or_default(),
&std::env::var("AELIO_SERVER_URL").unwrap_or_else(|_| "ws://127.0.0.1:3010".to_string()),
);
aelio.expose("getOrderStatus", "Get order status", "read", |args, ctx| {
Ok(serde_json::json!({
"orderId": args.get("orderId"),
"customerId": ctx.get("customerId"),
"status": "shipped"
}))
});
aelio.listen().await
}Step 4: Run
AELIO_SDK_SECRET=change-me-in-production \
AELIO_SERVER_URL=ws://127.0.0.1:3010 \
cargo runAxum integration
Run the SDK in a background Tokio task alongside your Axum server:
#[tokio::main]
async fn main() {
let mut aelio = AelioSDK::new(&secret, &url);
// ... expose tools ...
tokio::spawn(async move {
aelio.listen().await.expect("SDK connection failed");
});
let app = Router::new().route("/health", get(|| async { "ok" }));
axum::serve(listener, app).await.unwrap();
}Protocol reference
See SDK Wire Protocol for complete message schemas.
Reference implementations
- Python SDK:
sdk/python/sdk.py— ~330 lines - Protocol types:
packages/protocol/src/index.ts - Note: Rust is used for Sunjet/Astrolobe storage engine, not as a primary SDK language