Counting visitors…
Browse all docs
Start · 9Find your path through RunifoldLearn Runifold in 45 minutesUnderstand the complete Runifold platformYour first trustworthy runChoose the right execution APIChoose crates and Cargo featuresBuild common Runifold applicationsRunifold frequently asked questionsTroubleshoot Runifold applications
Execution kernel · 7Understand RunContextCoordinate external effects safelyBound work with budgets and cancellationHandle errors and retries safelyEvents, journals, and execution evidenceDesign capability-safe executionRecover safely from checkpoints
Models & providers · 7Route across models without duplicate outputChoose and configure a providerUse the provider-neutral model protocolBuild on the Provider Runtime contractUse OpenAI control-plane and Realtime APIsTest and benchmark provider adaptersSet up OpenAI, Anthropic, Gemini, and Ollama
Agents · 7Build and configure an AgentGive an Agent typed toolsAdd conversations and semantic memoryDelegate to child Agents safelyReturn structured Rust valuesStream without losing semanticsGround an Agent with retrieval
Durable workflows · 7Compose deterministic workflowsMake workflows durableOperate durable workflow workersCoordinate timers, signals, and durable waitsRun multi-tenant workflow infrastructureRun parallel branches and safe racesVersion and evolve durable workflows
Integrations · 7Connect through MCPChoose stores and persistence boundariesExpose durable work through MCP TasksBuild and evaluate retrieval pipelinesUse MCP Resources, Prompts, and SamplingCache MCP responses without crossing authorityDeploy Runifold in a Rust web service
Quality & operations · 10Test without the networkEvaluate quality and prevent regressionsObserve the complete run treeRun safely in browsers and at the edgeRead reliability claims preciselyRun reproducible evaluations in CIOperate Runifold with SLOsGovern Task retention and deletionArchive audit evidence to S3-compatible WORM storageManage compatibility and trusted releases
Docs/Durable workflows
NEW TO RUNIFOLD?Build the complete mental model in 45 minutes
Durable workflows

Coordinate timers, signals, and durable waits

Suspend workflows without holding a process, deliver idempotent signals, govern long waits, and resume safely.

Practical guide·12 min

Why waits are durable state

Long business processes spend most of their lifetime waiting: a delivery date, approval, webhook, human answer, or external Batch. Keeping a Worker or async task alive wastes capacity and loses state on restart.

A durable wait commits why execution paused and what event may wake it. The Worker releases ownership; another process can resume later.

Timers

A Timer records a store-authoritative wake time. Claim queries do not return it before that instant. When due, it becomes ready without relying on a process sleep or in-memory scheduler.

Use Timers for retry schedules, reminders, polling intervals, and business deadlines. Keep operation timeouts separate: a Timer decides when to resume, while a timeout bounds one attempt.

Signals

A Signal is external input addressed to a waiting Workflow. Give every delivery a stable identity so network retries cannot create duplicate transitions. Store Signals durably and scope them to tenant and checkpoint.

Define behavior for early, duplicate, unknown, and late Signals. Never let an opaque Task or Signal ID act as authorization by itself.

Interrupts

An interrupt moves a Workflow to explicit input_required state. It identifies the requested decision and accepted response shape. Approve, edit, reject, or cancel commands must be idempotent.

Human review is not a special prompt. It is a durable protocol boundary with identity, authorization, retention, and auditable resolution.

Retention and governance

Bound wait duration, pending Signal count, polling frequency, and retained history. Supervisors should find stale waits and surface them without inventing completion.

Protocol TTL, execution deadline, business due date, and physical deletion retention are distinct concepts. Model each separately; converting one into another can cancel live work or preserve sensitive state indefinitely.

Build timer, signal, and review waits

use std::time::Duration;
 
let workflow = Workflow::builder("approval")
    .version(1)
    .step("prepare", PrepareProposal, CapabilitySet::new())
    .wait_for_signal_or_timeout(
        "wait_for_documents",
        "documents_ready",
        Duration::from_secs(24 * 60 * 60),
    )
    .interrupt("manager_review", "Approve, edit, or reject this proposal")
    .timer("cooldown", Duration::from_secs(30))
    .step("commit", CommitDecision, write_capabilities)
    .build()?;

The worker checkpoints the wait before releasing its lease. Publish Signals through WorkflowStore with a stable WorkflowSignalId, authenticated tenant, checkpoint ID, name, and payload. Treat duplicate delivery as the same event. For interrupts, render the keyed input request and submit one idempotent approve/edit/reject decision; never infer approval from a missing response.