resonate-basic-ephemeral-world-usage-rust — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited resonate-basic-ephemeral-world-usage-rust (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
v0.1.0 caveat. The Resonate Rust SDK is in active development. It is NOT yet published on crates.io; install as a git dependency. APIs may change between releases. Treat every code example as a moving target until the SDK reaches 1.0.
The ephemeral world is anywhere your Rust program starts: main(), an Axum or Actix handler, a CLI entry point, a background service. You use the Resonate client to register durable functions and invoke them top-level. Once an invocation starts, it crosses into the durable world and Resonate guarantees its completion — retries on failure, resumes after crashes, continues across process restarts.
This skill covers the Client API surface. The Durable World (Context APIs inside #[resonate::function]) lives in resonate-basic-durable-world-usage-rust.
The Rust SDK is a git dependency (v0.1.0; not on crates.io yet):
[dependencies]
resonate = { git = "https://github.com/resonatehq/resonate-sdk-rs", branch = "master" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }Most programs also want serde_json for the json!(...) macro used in the promises API.
use resonate::prelude::*;Local mode (zero-dependency; in-memory promise store):
use resonate::prelude::*;
let resonate = Resonate::local();Remote mode (connects to a Resonate server):
use resonate::prelude::*;
let resonate = Resonate::new(ResonateConfig {
url: Some("http://localhost:8001".into()),
..Default::default()
});With explicit worker group + auth token:
let resonate = Resonate::new(ResonateConfig {
url: Some("https://resonate.example.com".into()),
group: Some("workers".into()),
token: std::env::var("RESONATE_TOKEN").ok(),
..Default::default()
});The SDK reads environment variables when config fields are not set:
RESONATE_URL — full base URLRESONATE_HOST + RESONATE_PORT — alternate constructionRESONATE_TOKEN — JWT for authenticated serversRESONATE_SCHEME — http by defaultRESONATE_PREFIX — prepended to all promise + task IDs for multi-tenant namespacingThe #[resonate::function] attribute macro transforms an async function into a durable function. The SDK infers the function kind from the first parameter:
| First parameter | Kind | Semantics |
|---|---|---|
&Context | Workflow | Can orchestrate sub-tasks via ctx.run, ctx.rpc, ctx.sleep |
&Info | Leaf with metadata | Read-only access to execution metadata (id, parent, tags) |
anything else (e.g., String, OrderInput) | Pure leaf | Stateless computation; no context access |
All durable functions MUST return resonate::error::Result<T> (aliased to Result<T> when you use resonate::prelude::*):
use resonate::prelude::*;
#[resonate::function]
async fn process_order(ctx: &Context, order_id: String) -> Result<String> {
let order = ctx.run(load_order, order_id.clone()).await?;
Ok(format!("processed {}", order))
}
#[resonate::function]
async fn load_order(order_id: String) -> Result<String> {
// pure leaf — no context, no Info
Ok(format!("order-{}", order_id))
}Override the registered name for RPC cross-process identification:
#[resonate::function(name = "order-processor")]
async fn process_order(ctx: &Context, order_id: String) -> Result<String> {
// ...
}Dependencies (DB pools, HTTP clients, config) are attached to the Resonate instance at construction time with the .with_dependency<T>(value) builder. The SDK uses Rust's type system (not a string key) — you retrieve a dependency by its type inside a durable function via ctx.get_dependency::<T>().
use std::sync::Arc;
use sqlx::PgPool;
struct AppConfig {
api_key: String,
}
#[tokio::main]
async fn main() -> Result<()> {
let pool = PgPool::connect(&std::env::var("DATABASE_URL")?).await?;
let config = AppConfig { api_key: std::env::var("API_KEY")? };
let resonate = Resonate::new(ResonateConfig::default())
.with_dependency(pool)
.with_dependency(config);
resonate.register(charge_card).unwrap();
// ...
}
#[resonate::function]
async fn charge_card(ctx: &Context, order_id: String) -> Result<()> {
let pool = ctx.get_dependency::<PgPool>(); // Arc<PgPool>
let config = ctx.get_dependency::<AppConfig>(); // Arc<AppConfig>
// use pool + config in a leaf (see resonate-basic-durable-world-usage-rust)
Ok(())
}Type-dispatched DI means you can only have one dependency per type per Resonate instance; for multiple values of the same logical type, wrap them in distinct newtypes (struct PrimaryDb(PgPool) vs struct AnalyticsDb(PgPool)).
Note:with_dependencyis a pub fn onResonatein the v0.1.0 SDK source (resonate-sdk-rs:resonate/src/resonate.rs:271) but not mentioned indocs/develop/rust.mdxas of April 2026. Use with the understanding that docs lag source here.
Registration exposes a function to the Resonate system. Use .unwrap() or handle the Result (registration fails if the name is duplicated):
let resonate = Resonate::local();
resonate.register(process_order).unwrap();
resonate.register(load_order).unwrap();One .register call per function per process. Register everything your process should handle before invoking anything.
let result: String = resonate
.run("invocation-id", process_order, "order-123".into())
.await?;The builder accepts options before .await:
use std::time::Duration;
let result: String = resonate
.run("order:123", process_order, "order-123".into())
.timeout(Duration::from_secs(60))
.version(2)
.tags([("tenant".to_string(), "acme".to_string())].into())
.await?;resonate.run is the synchronous ephemeral-world entry. For async lifecycles where you want a handle, use .get(id) later.
let result: String = resonate
.rpc("invocation-id", "process_order", "order-123".into())
.target("poll://any@workers")
.await?;The target option routes to a worker group; the called function must be registered in a process that polls that target.
The Rust SDK has first-class cron scheduling:
let schedule = resonate
.schedule("daily-reconciliation", "0 2 * * *", "reconcile", "2026-04-16".into())
.await?;
// ...later, to delete:
schedule.delete().await?;(Python SDK does not expose a top-level .schedule(...) at v0.6.7; Rust does at v0.1.0. Cross-SDK parity is not yet achieved — see Related notes below.)
let mut handle = resonate.get::<String>("invocation-id").await?;
let result = handle.result().await?;The type parameter on .get::<T> deserializes the result; you need to know or agree on T with the invocation site.
External promises let code outside the durable function resolve or reject it — the primitive for human-in-the-loop workflows and webhook-driven resumption:
use serde_json::json;
// create a promise with a timeout (ms since Unix epoch), initial param, and tags
let timeout_at_ms: i64 = (std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_millis() as i64) + 30 * 60 * 1000; // 30 minutes from now
resonate.promises.create("approval:order-123", timeout_at_ms, json!({}), json!({})).await?;
// elsewhere (webhook, CLI, UI):
resonate.promises.resolve("approval:order-123", json!({"approved": true})).await?;
// or reject:
resonate.promises.reject("approval:order-123", json!({"reason": "over-budget"})).await?;
// or cancel (settles as rejected_canceled):
resonate.promises.cancel("approval:order-123", json!(null)).await?;
// query state:
let promise = resonate.promises.get("approval:order-123").await?;The data arguments are serde_json::Value; use the json! macro for ergonomic literals.
resonate.stop().await?;Stops background tasks (heartbeat, subscription loops). Good citizen for long-running daemons or CLI tools.
use resonate::prelude::*;
use tokio::signal;
#[tokio::main]
async fn main() -> Result<()> {
let resonate = Resonate::new(ResonateConfig {
url: std::env::var("RESONATE_URL").ok(),
group: Some("order-workers".into()),
..Default::default()
});
resonate.register(process_order).unwrap();
resonate.register(load_order).unwrap();
// wait for Ctrl+C; the SDK polls in the background
signal::ctrl_c().await?;
resonate.stop().await?;
Ok(())
}
#[resonate::function]
async fn process_order(ctx: &Context, order_id: String) -> Result<String> {
let order = ctx.run(load_order, order_id).await?;
Ok(order)
}
#[resonate::function]
async fn load_order(order_id: String) -> Result<String> {
Ok(format!("order-{}", order_id))
}Result. Use ? for concise propagation...Default::default() when partially constructing.Option<String> throughout.main must be async. #[tokio::main] sets up the runtime.std::time::Duration.Honest reading of v0.1.0 as of April 2026, verified against resonate-sdk-rs source (not just docs):
rust.mdxResonate::new/local, .register/run/rpc/schedule/get/stop/promises.*#[resonate::function] macro + 3 function kinds (Workflow / Leaf with Info / Pure leaf)ctx.run/rpc/sleep + .spawn() double-await patternctx.id/parent_id/origin_id/func_name/timeout_at.timeout(Duration), .target(&str) (context), .version/.tags (ephemeral)rust.mdx (use with eyes open)resonate.with_dependency<T>(value) — ephemeral-side DI (covered above)ctx.get_dependency::<T>() — durable-side DI (see resonate-basic-durable-world-usage-rust)ctx.promise::<T>() — Context-side HITL primitive (see resonate-basic-durable-world-usage-rust + resonate-human-in-the-loop-pattern-rust)ctx.info() — returns an Info struct with extra accessors branch_id and tagsThese are pub fn with docstring-level examples in the source; they appear intended for users, just not yet covered by the docs. Until rust.mdx catches up, cite the source path (e.g., resonate/src/context.rs:115) when reviewers question whether the API exists.
ctx.detached — fire-and-forget; parallelism is via .spawn() insteadctx.random.random() / ctx.time.time() — no deterministic time/random helpersctx.panic() / ctx.assert() — use Rust's own panic!/assert! + Result propagationTreat the last three as "may land in future versions; verify when v0.2.0+ ships."
resonate-basic-durable-world-usage-rust — Context APIs, #[resonate::function] mechanics, .spawn() for parallelism, builder optionsresonate-basic-debugging-rust — Rust-specific failure modes: v0.1.0 caveat, git-dep install issues, serde errors, Result<T> handlingdurable-execution + resonate-philosophy — foundational concepts; read these first if new to Resonateresonate-basic-ephemeral-world-usage-typescript + -python — sibling SDKs' ephemeral-world surfaces for comparison~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.