Rust
Use the Rust HTTP SDK to publish events, sign auth responses, manage history, annotations, and push.
Install
[dependencies]
sockudo-http = "2.1.0"
tokio = { version = "1", features = ["full"] }
sonic-rs = "0.5"Configure
use sockudo_http::{Config, Sockudo};
let config = Config::builder()
.app_id("app-id")
.key("app-key")
.secret("app-secret")
.host("127.0.0.1")
.port(6001)
.use_tls(false)
.build()?;
let sockudo = Sockudo::new(config)?;The default TLS backend is rustls and encryption support is enabled by default.
Create one Sockudo value at startup and share it with application state; its
HTTP client is designed for reuse.
Important builder options include timeout, pool_max_idle_per_host,
enable_retry, and max_retries. Use TLS outside local development and set
the SDK timeout below the surrounding request or job deadline.
Publish
use sockudo_http::{events::TriggerParams, Channel};
use sonic_rs::json;
let channels = vec![Channel::from_string("orders")?];
let params = TriggerParams::builder()
.idempotency_key("order-created-ord_123")
.build();
sockudo
.trigger(&channels, "order.created", json!({ "id": "ord_123" }), Some(params))
.await?;Exclude an originating socket and attach a stable idempotency key with
TriggerParams:
let params = TriggerParams::builder()
.socket_id("123.456")
.idempotency_key("order-paid:ord_123:v3")
.build();
sockudo
.trigger(
&channels,
"order.updated",
json!({ "id": "ord_123", "status": "paid" }),
Some(params),
)
.await?;Use a business-derived idempotency key for any operation that may be retried. Do not generate a new key on each attempt.
Batch and tags
use sockudo_http::events::BatchEvent;
use sonic_rs::json;
use std::collections::HashMap;
let mut tags = HashMap::new();
tags.insert("tenant".to_string(), "acme".to_string());
let event = BatchEvent::new("order.created", "orders", json!({ "id": "ord_124" }))
.with_tags(tags);
sockudo.trigger_batch(vec![event]).await?;Tags are strings and require server-side tag filtering to be enabled. Avoid putting secrets or high-cardinality unbounded data in tags.
Channel and user authentication
Perform application authorization before asking the SDK to sign:
use sockudo_http::{Channel, SockudoError};
use sonic_rs::json;
fn authorize_private(
sockudo: &Sockudo,
socket_id: &str,
channel_name: &str,
) -> Result<sockudo_http::SocketAuth, SockudoError> {
let channel = Channel::from_string(channel_name)?;
sockudo.authorize_channel(socket_id, &channel, None)
}
let presence = Channel::from_string("presence-lobby")?;
let member = json!({
"user_id": "user-42",
"user_info": { "name": "Ada" }
});
let auth = sockudo.authorize_channel("123.456", &presence, Some(&member))?;The HTTP handler must derive channel access and presence identity from its
authenticated session. For user-targeted events, use authenticate_user and
send_to_user; use terminate_user_connections or force_reconnect_user
when access changes.
History and pagination
use sockudo_http::HistoryParams;
let page = sockudo
.channel_history_with_name(
"orders",
Some(&HistoryParams {
limit: Some(50),
direction: Some("newest_first".to_owned()),
..Default::default()
}),
)
.await?;
if let Some(cursor) = page.next_cursor {
let next_page = sockudo
.channel_history_with_name(
"orders",
Some(&HistoryParams {
cursor: Some(cursor),
..Default::default()
}),
)
.await?;
process_history(next_page)?;
}Keep pages bounded and treat the cursor as opaque. Presence history and snapshots use the corresponding typed parameter structs.
Webhooks
Validate the signature against the unchanged request body before processing events:
use std::collections::BTreeMap;
fn verified_events(
sockudo: &Sockudo,
headers: &BTreeMap<String, String>,
raw_body: &str,
) -> Result<Vec<sockudo_http::WebhookEvent>, SockudoError> {
let webhook = sockudo.webhook(headers, raw_body);
if !webhook.is_valid(None) {
return Err(SockudoError::Validation {
message: "invalid webhook signature".to_owned(),
});
}
Ok(webhook.get_events()?)
}Do not deserialize, normalize, or log the body before verification. Enqueue validated events and acknowledge quickly.
End-to-end encrypted channels
Set encryption_master_key_base64 on the config builder and publish to a
private-encrypted-* channel. The SDK encrypts the event and returns a derived
shared secret during channel auth. Keep the 32-byte master key in a secret
manager and continue to use TLS.
Push notifications
use sonic_rs::json;
sockudo.activate_device(&json!({
"deviceId": "ios-device-1",
"clientId": "user-42",
"platform": "apns",
"providerToken": "provider-token"
})).await?;
sockudo.upsert_channel_push_subscription(&json!({
"deviceId": "ios-device-1",
"clientId": "user-42",
"channel": "orders"
}), None).await?;
let response = sockudo.publish_push(&json!({
"recipients": [{ "type": "channel", "channel": "orders" }],
"payload": {
"title": "Order updated",
"body": "Order ord_123 is packed"
},
"idempotency_key": "push-order-ord_123-packed"
})).await?;Use Rust SDK push helpers for backend services that already own transactional state and need typed error handling around notification fanout.
Errors, retries, and graceful shutdown
All fallible methods return Result<T, SockudoError>. Match the error category
when policy differs:
match sockudo
.trigger(&channels, "order.created", payload, Some(params))
.await
{
Ok(response) => record_status(response.status()),
Err(SockudoError::Request(error)) => schedule_retry(error)?,
Err(SockudoError::Validation { message }) => {
tracing::warn!(error = %message, "sockudo publish rejected");
}
Err(error) => return Err(error),
}Retries must be bounded and use an idempotency key. Share the SDK client rather
than constructing it per request, do not hold application locks across
.await, and use your service's cancellation mechanism to stop accepting new
jobs before shutdown. Never log signed URLs, app secrets, event payloads, or
raw webhook content.