# Durable Workflow Rust SDK
`durable-workflow` is the first-party Rust SDK for Durable Workflow workers
and clients. It can register workflow and activity handlers, long-poll the
worker protocol, start, signal, and query workflow executions, expose named
read-only query handlers, heartbeat workers and activities, and exchange
JSON-native payloads through the platform's generic Avro wrapper.
## Install
Add the exact crates.io release with Cargo:
```sh
cargo add durable-workflow@0.1.1 --exact
```
Or add the same exact requirement directly to `Cargo.toml`:
```toml
[dependencies]
durable-workflow = "=0.1.1"
```
Version `0.1.1` requires Rust `1.86` or newer. Snapshot query transport is
available from `0.1.1`; replayed workflow-instance state queries are the
direct-successor `0.1.2` surface.
## Compatibility
| `0.1.0` | `>=0.2,<0.3` | `1.2` | `2` |
| `0.1.1` | `>=0.2,<0.3` | `1.2` (snapshot queries require `1.8`) | `2` |
| `0.1.2+` | `>=0.2,<0.3` | `1.2` (replayed queries require `1.8`) | `2` |
The machine-readable values live in `[package.metadata.durable-workflow]` in
`Cargo.toml` as `supported-server-versions`, `worker-protocol-version`, and
`control-plane-version`. Query-capable releases also publish `query-tasks`,
`query-task-minimum-worker-protocol-version`, `replayed-instance-state-queries`,
`query-state-model`, `snapshot-inspection-queries`, and `payload-codecs`. Existing
worker operations retain the `1.2` baseline; only query-task poll, complete,
and fail requests use the additive `1.8` feature floor. The server's advertised
protocol manifests remain authoritative when checking compatibility during
deployment.
## Worker
```rust
use durable_workflow::{json, Client, Result, Worker};
#[derive(Clone, Default)]
struct HelloState {
started_by: Option<String>,
}
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::builder("http://127.0.0.1:8080")
.token(std::env::var("DURABLE_WORKFLOW_TOKEN").ok())
.namespace("default")
.build()?;
let mut worker = Worker::new(client.clone(), "rust-workers");
worker.register_activity("hello.activity", |ctx, args| async move {
ctx.heartbeat(json!({"stage": "started"})).await?;
let name = args.first().and_then(|value| value.as_str()).unwrap_or("world");
Ok(json!(format!("hello, {name}")))
});
worker.register_replayed_workflow("hello.workflow", HelloState::default, |ctx, _input, state| async move {
let signal = ctx.wait_signal("start").await?;
let name = signal.first().and_then(|value| value.as_str()).unwrap_or("world");
state.update(|current| current.started_by = Some(name.to_string()))?;
let greeting = ctx.activity("hello.activity", json!([name])).await?;
Ok(json!({"greeting": greeting}))
});
worker.register_replayed_query::<HelloState, _, _>("hello.workflow", "started-by", |_ctx, state, _args| async move {
Ok(json!(state.started_by))
});
worker.run().await
}
```
## Client
```rust
# use durable_workflow::{json, Client, Result};
# async fn example(client: Client) -> Result<()> {
let handle = client
.start_workflow("hello.workflow", "rust-workers", "hello-rust-1", json!([]))
.await?;
client
.signal_workflow(&handle.workflow_id, "start", json!(["Rust"]))
.await?;
let started_by = handle.query("started-by", json!([])).await?;
assert_eq!(started_by, json!("Rust"));
let output = handle.result(Default::default()).await?;
# println!("{output}");
# Ok(())
# }
```
## Queries
`Worker::register_replayed_workflow` gives ordinary workflow execution a typed
`WorkflowInstance<S>`. Put transitions after activity and signal resolution in
that workflow closure. `Worker::register_replayed_query` re-runs the same
closure over committed durable history, then invokes the named query with an
immutable, detached `Arc<S>`. This is the recommended workflow-instance query
API: query code does not parse history or duplicate transition logic.
Replay-generated commands are discarded. A query handler has no command API,
and its detached state is never retained, so successful and failed queries do
not append history, advance execution, or change a later query. The same query
serves running, restarted, and successfully completed workflows:
```rust
# use durable_workflow::{json, Client, Worker};
# #[derive(Clone, Default)]
# struct CounterState { count: i64 }
# fn configure(client: Client) {
let mut worker = Worker::new(client, "counter-workers");
`Client::heartbeat_activity_task` directly.
## Example
`examples/hello_world.rs` contains a complete round trip: it registers a Rust
worker, starts a workflow, sends a signal, runs an activity, heartbeats that
activity, exposes a named query, and waits for the completed result.
With a Durable Workflow server running locally:
```sh
DURABLE_WORKFLOW_SERVER_URL=http://127.0.0.1:8080 \
DURABLE_WORKFLOW_TOKEN=your-token \
cargo run --example hello_world
```
`TASK_QUEUE` optionally overrides the default `rust-workers` task queue.
## API documentation
The complete API reference is published at
[rust.durable-workflow.com](https://rust.durable-workflow.com/). Documentation
for `main` is rebuilt and deployed automatically.
## Ownership and versioning
The Durable Workflow project owns and maintains the crate. This repository is
the authoritative source for the `durable-workflow` crate and its Rust API
documentation.
Crate releases follow semantic versioning and are tagged with the exact crate
version, such as `0.1.1`. Rust SDK versions are independent from Durable
Workflow server image versions. A compatible server range is declared in
package metadata instead of coupling crate publication to a server release.