Resonate basic ephemeral world usage rust
Skill resonatehq/resonate-skills/resonate-basic-ephemeral-world-usage-rust
Agent skills for building with Resonate — durable execution for long-running, crash-safe workflows.
npx -y skills add resonatehq/resonate-skills --skill resonate-basic-ephemeral-world-usage-rustAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Core patterns for using the Resonate Rust SDK's Client APIs from the ephemeral world — initializing, registering durable functions with the #[resonate::function] attribute, invoking them top-level (run / rpc / schedule), getting handles, and managing external promises. Use when writing any Rust binary or process-level code that needs to launch or coordinate Resonate workflows. v0.1.0 caveat: APIs may change between releases.
The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
12.7 KB, as published. Nobody here has run it
Resonate Basic Ephemeral World Usage — Rust
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.
Overview
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.
Install
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::*;
Initialize
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—httpby defaultRESONATE_PREFIX— prepended to all promise + task IDs for multi-tenant namespacing
Define a durable function
The #[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> {
// ...
}
Share resources via dependencies
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.
Register
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.
Invoke a durable function
Same process, synchronous (awaits result)
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.
Remote process, synchronous
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.
Scheduled (cron) invocation
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.)
Subscribe to an existing invocation
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
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.
Graceful shutdown
resonate.stop().await?;
Stops background tasks (heartbeat, subscription loops). Good citizen for long-running daemons or CLI tools.
Complete example: a worker binary
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))
}
Distinct Rust idioms
Result<T>everywhere — both Client API methods and durable functions returnResult. Use?for concise propagation.ResonateConfig { ... , ..Default::default() }— Rust's struct-update syntax; always include..Default::default()when partially constructing.Some(...)/.ok()/std::env::var(...).ok()for optional config — the SDK acceptsOption<String>throughout.tokio::main— durable functions are async; yourmainmust be async.#[tokio::main]sets up the runtime.serde_json::json!macro for promise data — explicit serialization; matches Rust's strong-typing discipline.Duration::from_secs(N)/from_millis(N)for timeouts — not raw numbers. Usestd::time::Duration.- Turbofish syntax on
.rpc::<T>(...)and.get::<T>(...)— Rust's type inference needs help when the result type isn't constrained by the call site.
Rust SDK API coverage status
Honest reading of v0.1.0 as of April 2026, verified against resonate-sdk-rs source (not just docs):
In the SDK source + documented in rust.mdx
Resonate::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 pattern- Context accessors
ctx.id/parent_id/origin_id/func_name/timeout_at - Builder options
.timeout(Duration),.target(&str)(context),.version/.tags(ephemeral)
In the SDK source BUT not in rust.mdx (use with eyes open)
resonate.with_dependency<T>(value)— ephemeral-side DI (covered above)ctx.get_dependency::<T>()— durable-side DI (seeresonate-basic-durable-world-usage-rust)ctx.promise::<T>()— Context-side HITL primitive (seeresonate-basic-durable-world-usage-rust+resonate-human-in-the-loop-pattern-rust)ctx.info()— returns anInfostruct with extra accessorsbranch_idandtags
These 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.
NOT in the SDK source at v0.1.0
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 ownpanic!/assert!+Resultpropagation
Treat the last three as "may land in future versions; verify when v0.2.0+ ships."
Related skills
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