Rust SDK
The SyVault Rust SDK is in alpha (v0.1.0) and not yet published to crates.io. The crate name (syvault-sdk) will be published when the SDK reaches a stable release. Track progress in sdks/rust/.
The SyVault Rust SDK (syvault-sdk) wraps the same ECDSA/ECDH/AES-GCM primitives used by the sy CLI. It gives you native-speed secret access with compile-time safety.
Installation
Add the crate to your Cargo.toml:
[dependencies]
syvault-sdk = "0.1"
tokio = { version = "1", features = ["full"] }
Authentication
use syvault_sdk::SyVaultClient;
use std::env;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = SyVaultClient::builder()
.server("https://vault.example.com")
.client_id("c9a1b2d3-4e5f-6789-abcd-ef0123456789")
.client_secret(&env::var("SY_CLIENT_SECRET")?)
.build()
.await?;
Ok(())
}
The client generates ECDSA P-256 signed JWTs internally and refreshes them before expiry.
Fetching Secrets
List all secrets
let secrets = client.secrets().list().await?;
for secret in &secrets {
println!("{} {}", secret.uid, secret.title);
}
Get a full secret
let secret = client.secrets().get("7Kj9mNpQ2xRs").await?;
println!("{}", secret.field("host").unwrap()); // "db.example.com"
println!("{}", secret.field("password").unwrap()); // "s3cret!"
Resolve a notation
let password = client
.secrets()
.notation("sy://Production/Database/field/password")
.await?;
println!("{password}"); // "s3cret!"
Creating Secrets
use syvault_sdk::models::CreateSecretInput;
use std::collections::HashMap;
let mut fields = HashMap::new();
fields.insert("key".into(), "sk_live_abc123".into());
fields.insert("environment".into(), "production".into());
let new_secret = client.secrets().create(CreateSecretInput {
folder_id: "folder-uuid-here".into(),
title: "Stripe API Key".into(),
fields,
}).await?;
println!("{}", new_secret.uid); // "Xp4qRtY8mNwK"
Rotating Secrets
let result = client.secrets().rotate("7Kj9mNpQ2xRs").await?;
println!("Status: {}", result.status); // "completed"
println!("New password: {}", result.new_fields["password"]);
Error Handling
The SDK uses a typed error enum:
use syvault_sdk::error::SyVaultError;
match client.secrets().get("nonexistent").await {
Ok(secret) => println!("Got: {}", secret.title),
Err(SyVaultError::SecretNotFound { uid }) => {
eprintln!("Secret {uid} does not exist");
}
Err(SyVaultError::Auth(msg)) => {
eprintln!("Authentication failed: {msg}");
}
Err(e) => return Err(e.into()),
}
Caching
Configure the in-memory cache at construction time:
use std::time::Duration;
let client = SyVaultClient::builder()
.server("https://vault.example.com")
.client_id("...")
.client_secret("...")
.cache_ttl(Duration::from_secs(300))
.cache_max(500)
.build()
.await?;
Disable caching by setting cache_ttl to zero:
let client = SyVaultClient::builder()
// ...
.cache_ttl(Duration::ZERO)
.build()
.await?;
Axum Example
Inject database credentials into an Axum web server:
use axum::{routing::get, Router, Json};
use serde_json::json;
use sqlx::postgres::PgPoolOptions;
use syvault_sdk::client::SyVaultClient;
use std::env;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let sy = SyVaultClient::builder()
.server(&env::var("SY_SERVER")?)
.client_id(&env::var("SY_CLIENT_ID")?)
.client_secret(&env::var("SY_CLIENT_SECRET")?)
.build()
.await?;
let secret = sy.secrets().get("7Kj9mNpQ2xRs").await?;
let pool = PgPoolOptions::new()
.max_connections(10)
.connect(&format!(
"postgres://{}:{}@{}:{}/{}",
secret.field("username").unwrap(),
secret.field("password").unwrap(),
secret.field("host").unwrap(),
secret.field("port").unwrap(),
secret.field("database").unwrap(),
))
.await?;
let app = Router::new().route("/health", get(|| async { Json(json!({"ok": true})) }));
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
axum::serve(listener, app).await?;
Ok(())
}
Safety and Threading
SyVaultClient implements Send + Sync and can be shared across tasks freely using Arc<SyVaultClient> or by cloning (the inner state is Arc-wrapped). The HTTP connection pool, JWT cache, and secret cache are all thread-safe.
use std::sync::Arc;
let sy = Arc::new(client);
// Safe to clone into spawned tasks
let sy2 = Arc::clone(&sy);
tokio::spawn(async move {
let secret = vf2.secrets().get("abc123").await.unwrap();
println!("{}", secret.title);
});