How Workflow Runs Use TinyFlows Durable Graphs with Checkpointing in OpenHuman
OpenHuman persists workflow executions as TinyFlows durable graphs by using a SQLite-backed checkpointer that records every node output to a journaled checkpoint file, enabling runs to resume from the exact state after crashes or interruptions.
Workflow runs in the OpenHuman automation engine leverage TinyFlows durable graphs to create fault-tolerant, resumable execution pipelines. This integration stores complete graph states—including node outputs, metadata, and execution context—to a local SQLite database, allowing interrupted processes to resume from precise checkpoints rather than restarting from scratch.
The Durable Graph Architecture
OpenHuman structures workflow runs around capabilities (caps) that define the graph's behavior. The high-level entry point open_flow_checkpointer, re-exported from src/openhuman/flows/tinyflows/mod.rs, orchestrates the creation of durable execution contexts.
The system compiles a set of capability adapters into a runnable graph:
- Memory nodes for state persistence between steps
- HTTP adapters for external API calls
- LLM adapters for language model invocations
- Native tool sets for built-in operations
These components are assembled via build_capabilities in src/openhuman/flows/tinyflows/caps/mod.rs, producing a Graph instance that understands how to serialize its state for durability.
Checkpoint Persistence Layer
When a run initiates, the engine instantiates a SqliteCheckpointer defined in src/openhuman/flows/tinyflows/checkpoint_sqlite.rs. This implementation of the TinyFlows Checkpointer trait writes a record for every graph node that produces output through checkpoint_writes.
All checkpoint data resides in a SQLite file at <workspace>/flows/checkpoints.db, maintaining the same on-disk format used prior to TinyFlows integration. Each row contains six critical fields:
thread_id– The TinyFlows run/thread identifier exposed to theFlowRunObservercheckpoint_id– A user-supplied or auto-generated identifier for the specific checkpointparent_checkpoint_id– References the preceding checkpoint, enabling parent-chain traversalrun_id– The overall flow run identifier grouping related checkpointsnamespace– A JSON-encoded isolation boundary per flowseq– Monotonically increasing sequence number for deterministic ordering
During execution, the engine calls put on the checkpointer to persist the current graph state, including metadata, next nodes, and the full Checkpoint<State> payload.
Runtime Integration and Resumption
The observability layer in src/openhuman/flows/tinyflows/observability.rs wraps runs with a journal recording start, checkpoint, and finish events. This layer forwards events to the FlowRunObserver, which receives the checkpointer's thread_id to correlate live runs with persisted storage.
If a process crashes or is manually stopped, the persisted state enables precise resumption:
- The runtime reads the latest checkpoint from
<workspace>/flows/checkpoints.db resume_with_checkpointer_journaled_observedreconstructs the graph state- Execution continues from the last successfully persisted node
This mechanism ensures that long-running workflows—such as multi-step LLM chains or complex data pipelines—maintain atomic progress guarantees.
Lifecycle Management and Pruning
The checkpointer implementation includes automated maintenance to prevent unbounded storage growth. According to the source code test suite, the system supports:
- Retention policies –
prune_keeps_the_newest_checkpointsremoves older entries when configured limits are exceeded - Thread deletion –
deleting_a_thread_removes_its_checkpoints_and_leaves_othersensures that deleting a specific thread identifier removes all associated rows without affecting other runs
These features allow production deployments to maintain predictable database sizes while preserving the durability guarantees required for critical automation workflows.
Implementation Example
The following Rust code demonstrates constructing a durable workflow with checkpointing:
use openhuman::flows::tinyflows::{
caps::{self, build_capabilities},
open_flow_checkpointer,
checkpoint_sqlite::SqliteCheckpointer,
};
use tinyflows::graph::{Graph, RunConfig};
// 1. Build the graph capabilities (IO adapters, LLM, HTTP, etc.)
let caps = build_capabilities(
caps::memory_node::MemoryNode::default(),
caps::http::HttpAdapter::default(),
caps::llm::LlmAdapter::default(),
caps::tools::NativeToolSet::default(),
);
// 2. Create a SQLite-backed checkpointer (workspace is the current OpenHuman workspace)
let cp = SqliteCheckpointer::open(":memory:").expect("in-memory checkpointer");
// 3. Assemble the run configuration (you can name the run, set a checkpoint id, …)
let cfg = RunConfig::default()
.with_checkpoint_id(Some("run-1".to_string()))
.with_thread_id("thread-42".to_string());
// 4. Start the flow with a checkpointer so every node write is persisted
let run = open_flow_checkpointer(caps, cp, cfg).expect("run created");
// 5. Execute the graph (example: a simple sequential flow)
run.run().await.expect("flow completed");
// 6. Later – resume from the last checkpoint
let resumed = open_flow_checkpointer(caps, cp, cfg)
.and_then(|r| r.resume())
.await
.expect("resumed run");
For persistent storage between process restarts, replace ":memory:" with the workspace path: SqliteCheckpointer::open("<workspace>/flows/checkpoints.db").
Summary
- OpenHuman integrates TinyFlows durable graphs through the
open_flow_checkpointerentry point insrc/openhuman/flows/tinyflows/mod.rs. - The SqliteCheckpointer persists every node output to
<workspace>/flows/checkpoints.dbwith metadata includingthread_id,checkpoint_id, and parent references. - The
Checkpointertrait implementation enables crash recovery viaresume_with_checkpointer_journaled_observedin the observability layer. - Checkpoint pruning and thread deletion keep storage bounded in production environments.
- Workflow runs combine capability adapters (memory, HTTP, LLM) with durable storage to create fault-tolerant automation pipelines.
Frequently Asked Questions
Where does OpenHuman store workflow checkpoint data?
Checkpoint data is stored in a SQLite database file located at <workspace>/flows/checkpoints.db within the current OpenHuman workspace. This path predates the TinyFlows integration, ensuring backward compatibility with existing deployment structures while storing the thread_id, checkpoint_id, and serialized graph state for every persisted node output.
How does resuming a workflow run work after a process crash?
The SqliteCheckpointer implementation allows the runtime to call resume_with_checkpointer_journaled_observed from src/openhuman/flows/tinyflows/observability.rs. This method reads the latest checkpoint row matching the run's thread_id and checkpoint_id, reconstructs the Checkpoint<State> payload including pending nodes and metadata, and resumes execution from the exact point of interruption without reprocessing completed steps.
What information is contained in each checkpoint row?
Each checkpoint row contains six columns: thread_id (the execution identifier), checkpoint_id (the specific save point), parent_checkpoint_id (for chain traversal), run_id (the overall flow identifier), namespace (JSON-encoded isolation data), and seq (monotonically increasing ordering sequence). Together, these fields enable precise state reconstruction and historical audit trails.
How does OpenHuman prevent checkpoint databases from growing indefinitely?
The checkpointer supports configurable pruning through retention policies validated by tests such as prune_keeps_the_newest_checkpoints, which removes older entries when limits are exceeded. Additionally, deleting_a_thread_removes_its_checkpoints_and_leaves_others ensures that cleaning up a specific workflow thread deletes only its associated rows, preserving other runs while reclaiming storage space.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →