# How Tinyflows-Powered Workflow Graphs Enable Durable Checkpointed Automation in OpenHuman

> Discover how tinyflows workflow graphs in OpenHuman create durable, checkpointed automation. Ensure exact resumption after any interruption with SQLite-backed state persistence.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: internals
- Published: 2026-08-27

---

**OpenHuman leverages the tinyflows engine to execute automation workflows as immutable directed graphs, persisting execution state after every node via SQLite-backed checkpoints that enable exact resumption after crashes, pauses, or system restarts.**

OpenHuman’s automation layer is built on the open-source tinyflows engine, which transforms visual workflow designs into durable, resumable execution graphs. By integrating tinyflows-powered workflow graphs with a persistent checkpointing layer, OpenHuman ensures that long-running automations survive interruptions and can be audited step-by-step.

## The Typed Directed Graph Architecture

At the core of OpenHuman’s workflow system is the **immutable directed graph** defined in `tinyflows::model::WorkflowGraph`. This structure describes nodes (triggers, agents, tools, and conditional branches) and their connections as a type-safe graph that can be validated before execution begins.

When a flow is saved, OpenHuman wraps the raw tinyflows graph in domain-specific types defined in [`src/openhuman/flows/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/types.rs). This wrapper adds essential metadata including a stable UUID, creation timestamps, and a reference to the **checkpoint** that tinyflows creates on each run. This separation between the pure graph model and OpenHuman’s persistence layer ensures that workflow definitions remain immutable while execution state evolves independently.

## The Three-Stage Execution Pipeline

The tinyflows engine processes every workflow through three well-defined stages that enforce safety before any side-effects occur.

### Validation via `validate_all`

Before compilation, the graph undergoes structural validation using `tinyflows::validate::validate_all`. This function guarantees structural correctness—such as ensuring exactly one trigger node exists—before any side-effects are allowed. Validation failures are caught early, preventing invalid workflows from ever reaching the execution phase.

### Compilation and Checkpoint Storage

Once validated, `tinyflows::compiler::compile` transforms the graph into an executable representation. The compiled form is immediately stored in a **checkpointer**, which can be either the default `tinyflows::graph::checkpoint::FileCheckpointer` or OpenHuman’s custom SQLite-backed implementation in [`src/openhuman/flows/tinyflows/checkpoint_sqlite.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/tinyflows/checkpoint_sqlite.rs). This compilation step ensures that the execution plan is serialized and ready for resumption at any point.

### Execution with Capability Adapters

The `tinyflows::engine::run` function walks the compiled graph, invoking **capability adapters** defined in [`src/openhuman/flows/tinyflows/caps.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/tinyflows/caps.rs). Each adapter implements a tinyflows trait—such as `HttpClient`, `MemoryProvider`, or `ToolInvoker`—by delegating to OpenHuman services. This architecture keeps the execution engine side-effect-free; actual HTTP requests, memory calls, and tool executions only occur when the adapters are invoked.

## Checkpointing for Durable Automation

The **checkpointer** is the foundation of durability in OpenHuman’s workflow engine. After every node finishes execution, tinyflows writes a checkpoint record containing the node ID, current execution state, and any output required for later resumption to the SQLite database via `tinyflows::graph::checkpoint`.

If a process crashes or a user pauses the flow at an approval gate, the stored checkpoint allows the engine to resume exactly where it left off using `tinyflows::graph::checkpoint::RunObserver::resume`. Because checkpoints are persisted on disk, runs survive restarts, system failures, or user-initiated pauses. The run history is later exposed via the RPC surface (`flows::ops::reconstruct_steps`) and the UI’s workflow canvas, providing a step-by-step replay of execution.

## Practical Implementation Examples

### Defining a Tinyflows Workflow Graph

```rust
use tinyflows::model::{Node, NodeKind, WorkflowGraph};

let nodes = vec![
    Node { 
        id: "trigger".into(), 
        kind: NodeKind::Trigger, 
        name: "Schedule".into(), 
        config: json!({"cron":"0 9 * * MON"}) 
    },
    Node { 
        id: "http".into(),    
        kind: NodeKind::Tool,    
        name: "HttpRequest".into(), 
        config: json!({"method":"GET","url":"https://api.example.com/report"}) 
    },
];
let edges = vec![ ("trigger".into(), "http".into()) ];

let graph = WorkflowGraph { nodes, edges };

```

*Source*: Tinyflows graph definition in the vendored `tinyflows` crate.

### Importing and Persisting in OpenHuman

```rust
use openhuman::flows::ops::{import_flow, persist_flow};

let import = import_flow(&graph).expect("validation ok");
let flow = persist_flow(import, workspace_id).expect("saved");
println!("Saved flow {} with id {}", flow.name, flow.id);

```

*Source*: [`src/openhuman/flows/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/ops.rs) handles validation wrapping and persistence logic.

### Running with Checkpointing

```rust
use openhuman::flows::tinyflows::caps::build_capabilities;
use tinyflows::engine::run;
use tinyflows::graph::checkpoint::FileCheckpointer;

// Build adapters that talk to OpenHuman services
let caps = build_capabilities(&core_context);

// Load the persisted checkpoint (or start fresh)
let checkpointer = FileCheckpointer::new("/tmp/flow_checkpoints.db");

// Compile the stored graph
let compiled = tinyflows::compiler::compile(&flow.graph).unwrap();

// Execute – each step writes a checkpoint
let outcome = run(&compiled, tinyflows::json::Value::Null, &caps)
    .with_checkpointer(&checkpointer)
    .execute()
    .await?;

```

*Source*: [`src/openhuman/flows/tinyflows/caps.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/tinyflows/caps.rs) provides the adapter builder, while [`src/openhuman/flows/tinyflows/checkpoint_sqlite.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/tinyflows/checkpoint_sqlite.rs) implements the durable storage backend.

### Resuming a Paused Flow

```rust
use tinyflows::graph::checkpoint::RunObserver;

// Retrieve the last checkpoint for this flow run
let observer = RunObserver::load(&checkpointer, run_id).await?;
let resumed_outcome = observer.resume(&compiled, &caps).await?;

```

*Source*: `tinyflows::graph::checkpoint` APIs and OpenHuman’s wrapper integration.

## Summary

- **Immutable graph model**: Workflows are stored as typed `tinyflows::model::WorkflowGraph` instances that ensure structural correctness before execution.
- **Three-stage safety**: Validation (`validate_all`), compilation (`compile`), and execution (`run`) occur sequentially to prevent invalid or unsafe workflows from running.
- **SQLite-backed durability**: The custom checkpointer in [`src/openhuman/flows/tinyflows/checkpoint_sqlite.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/tinyflows/checkpoint_sqlite.rs) persists state after every node, enabling exact resumption via `RunObserver::resume`.
- **Side-effect isolation**: Capability adapters in [`src/openhuman/flows/tinyflows/caps.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/tinyflows/caps.rs) map pure engine operations to OpenHuman services, keeping the core execution logic side-effect-free until adapters are invoked.

## Frequently Asked Questions

### What happens if a node fails during execution?

When a node fails, the checkpoint recorded immediately after the previous successful node remains intact. The workflow can be resumed from that last valid checkpoint after the underlying issue is resolved, without re-executing earlier steps. This behavior is managed by the `RunObserver` interface in `tinyflows::graph::checkpoint` and exposed through OpenHuman’s `flows::ops::reconstruct_steps` RPC surface.

### How does checkpointing differ from simple execution logging?

Checkpointing captures the complete serializable state required to resume execution—including node outputs and execution context—whereas logging merely records events. OpenHuman’s SQLite checkpointer stores this state durably in [`checkpoint_sqlite.rs`](https://github.com/tinyhumansai/openhuman/blob/main/checkpoint_sqlite.rs), allowing the engine to reconstruct the exact runtime environment and continue from any point, even after a system restart.

### Can workflows survive a complete system restart?

Yes. Because checkpoints are persisted to disk via `FileCheckpointer` or the SQLite-backed implementation, workflow runs survive process crashes, container restarts, and system reboots. The `RunObserver::load` method retrieves the last checkpoint state from disk, and `resume` continues execution exactly where it stopped.

### Why are capability adapters necessary in the OpenHuman integration?

Capability adapters in [`src/openhuman/flows/tinyflows/caps.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/tinyflows/caps.rs) implement tinyflows traits like `HttpClient` and `MemoryProvider`, bridging the pure, side-effect-free tinyflows engine with OpenHuman’s actual services. This separation ensures that the workflow graph itself remains immutable and testable, while side effects (HTTP calls, database writes) are isolated in adapter code that the engine invokes only during execution.