The official Rust SDK for the Verne Nautilus platform.
Server-side only. API keys carry full service access and must never be used in WASM or client-side contexts.
Rust 1.75 or later.
[dependencies]
vernesoft = "0.1"use vernesoft::Verne;
#[tokio::main]
async fn main() -> Result<(), vernesoft::Error> {
let verne = Verne::builder()
.relay(std::env::var("VERNE_RELAY_KEY").unwrap())
.gate(std::env::var("VERNE_GATE_KEY").unwrap())
.build()?;
Ok(())
}You can also instantiate services independently if you only need one:
use vernesoft::{Relay, Gate};
let relay = Relay::new("vrn_relay_live_sk_...");
let gate = Gate::new("vrn_gate_live_sk_...");Send events to all subscribed endpoints:
verne.relay()?.messages().send(vernesoft::SendMessageParams {
event_type: "user.created".into(),
payload: serde_json::json!({ "id": "usr_123" }),
..Default::default()
}).await?;Optional parameters:
verne.relay()?.messages().send(vernesoft::SendMessageParams {
event_type: "order.placed".into(),
payload: serde_json::json!({ "order_id": "999" }),
idempotency_key: Some("evt_abc".into()), // prevent duplicate delivery within 24h
channels: Some(vec!["team-a".into()]), // restrict to specific endpoint channels
}).await?;List previously sent events:
let page = verne.relay()?.messages().list(vernesoft::ListMessagesParams {
limit: Some(20),
event_type: Some("user.created".into()),
cursor: None,
}).await?;
println!("{:?}", page.data); // Vec<Message>
println!("{}", page.has_more); // bool
println!("{:?}", page.next_cursor); // pass to the next call to paginateManage your end-users. The tenant_id is automatically scoped to your API key.
// Create a user
let identity = verne.gate()?.identities().create(vernesoft::CreateIdentityParams {
schema_id: "user".into(),
traits: vernesoft::IdentityTraitsInput {
email: "user@example.com".into(),
custom_data: Some(serde_json::json!({ "role": "editor" })),
},
credentials: Some(serde_json::json!({
"password": { "config": { "password": "StrongPassword123!" } }
})),
state: Some("active".into()),
}).await?;
// Get a user
verne.gate()?.identities().get(&identity.id).await?;
// Update a user (JSON Patch — RFC 6902)
verne.gate()?.identities().patch(&identity.id, vec![
vernesoft::JsonPatchOp {
op: "replace".into(),
path: "/traits/custom_data/role".into(),
value: Some(serde_json::json!("admin")),
from: None,
},
]).await?;
// Delete a user
verne.gate()?.identities().delete(&identity.id).await?;Exchange your long-lived API key for a short-lived access token:
let token = verne.gate()?.tokens().create(vernesoft::CreateTokenParams {
subject: "usr_123".into(),
scopes: Some(vec!["gate.tokens.read".into()]), // optional
ttl_seconds: Some(3600), // optional, default 3600, max 86400
}).await?;
// token.access_token — attach to downstream requests
// token.expires_at — ISO 8601 expiryValidate a token:
let info = verne.gate()?.tokens().introspect(&token.access_token).await?;
if !info.active {
// token is expired or invalid
}Check whether a subject is allowed to perform an action:
let decision = verne.gate()?.authorize(vernesoft::AuthorizeParams {
subject: "usr_123".into(),
action: "relay.messages.read".into(),
resource: "tenant:ten_001".into(),
context: None,
}).await?;
if !decision.allowed {
eprintln!("Forbidden: {}", decision.reason);
}All errors are returned as vernesoft::Error:
use vernesoft::Error;
match verne.relay()?.messages().send(params).await {
Ok(msg) => println!("{}", msg.id),
Err(Error::Api(e)) => {
eprintln!("code: {}", e.code); // e.g. "invalid_payload", "unauthorized"
eprintln!("status: {}", e.status); // HTTP status code
eprintln!("request_id: {}", e.request_id); // include in support requests
}
Err(Error::Http(e)) => eprintln!("network error: {e}"),
Err(Error::Config(msg)) => eprintln!("config error: {msg}"),
Err(e) => eprintln!("error: {e}"),
}Both Verne and the per-service builders accept an optional timeout (default 30 seconds):
let verne = Verne::builder()
.relay(std::env::var("VERNE_RELAY_KEY").unwrap())
.timeout_secs(10)
.build()?;