OpenHuman Workflow System: TinyFlows and Visual Canvas Explained

OpenHuman’s workflow engine uses TinyFlows, a JSON-driven orchestration layer built in Rust that enables users to compose automation graphs via a drag-and-drop visual canvas, execute them with full security policy enforcement, and persist workflows in the local workspace.

OpenHuman is an open-source AI agent framework that combines a Rust Core with a React frontend to provide secure, local-first automation. The OpenHuman workflow system centers on TinyFlows, a lightweight engine that turns complex multi-step automations into portable JSON graphs editable through an interactive visual canvas.

What Is TinyFlows?

TinyFlows is the domain-specific orchestration engine residing in src/openhuman/flows/. It defines the data structures, execution semantics, and RPC interfaces that power every automated workflow in OpenHuman. Unlike monolithic pipeline tools, TinyFlows treats each step as a discrete tool invocation mapped to the same Tool trait used throughout the application—ensuring consistency between ad-hoc agent actions and structured workflows.

Core Architecture of the OpenHuman Workflow System

The architecture spans five distinct layers that bridge the Rust backend and React frontend.

Domain Definition Layer

The foundational data structures live in src/openhuman/flows/tinyflows/types.rs. This module defines the Workflow, Node, Edge, Trigger, and Condition types that serialize to JSON and represent the complete graph structure. Every workflow stored in the user's workspace (.openhuman/workspace/flows/) conforms to these Rust-defined schemas, ensuring type safety across the boundary between Core and UI.

Runtime Engine

The execution logic is implemented in src/openhuman/flows/tinyflows/engine.rs. The main run_workflow loop iterates over the graph, resolves each node's tool mapping, and invokes the appropriate implementation. The engine respects security tiers, sandbox modes, and tool-group advertising, streaming progress events back to the UI via WebSocket.

Tool Integration

TinyFlows reuse the existing agent tool ecosystem (tinytools). The registry in src/openhuman/tools/ops.rs exposes all_tinyflows_agent_tools(), which maps node types—such as http_request, rhai_eval, or run_workflow—to their concrete implementations. During execution, the engine looks up the tool by name, checks its required permissions against the active SecurityPolicy, and invokes it with the node's arguments.

Persistence and RPC Layer

Workflow definitions are persisted as JSON files and accessed through JSON-RPC endpoints defined in src/openhuman/flows/tinyflows/schemas.rs. Methods like flow_update and run_flow validate payloads, load definitions from disk, and hand them to the engine. The Harness::load_workflow function (see src/openhuman/flows/tinyflows/ops.rs) handles the actual filesystem interaction.

Visual Canvas Frontend

The React frontend (app/src/components/FlowCanvas.tsx) renders the graph using react-flow-renderer. Nodes appear as draggable cards displaying tool names and parameters; edges are SVG connectors that can be rewired interactively. The client-side service in app/src/lib/flow/flowService.ts translates canvas interactions—such as dragging nodes or editing properties—into RPC calls via coreRpcClient.applyPatch, ensuring the UI state remains synchronized with the persisted JSON definition.

Execution Flow: From Canvas to Completion

A complete workflow lifecycle follows these five stages:

  1. Create or Edit – The canvas loads the flow JSON via coreRpcClient.getFlow and renders the node graph. Users drag tools from the palette and wire them together.
  2. Save Changes – Every edit triggers applyPatch, which sends a JSON-RPC flow_update request. The Core validates the patch, writes to the workspace directory, and broadcasts a flow_updated event to all connected clients.
  3. Run – The front-end sends an run_flow RPC. The Core’s run_workflow handler loads the definition, builds an execution context (security tier, sandbox mode, tool group), and starts the engine.
  4. Engine Loop – For each node, the engine resolves the tool from the registry, checks policy in src/openhuman/tools/policy.rs, invokes the tool, and records the result. Progress events stream back via the workflow_progress WebSocket channel.
  5. Completion – The engine returns a WorkflowResult upon success or error, which the canvas displays. Completed nodes highlight in green; errors highlight in red with tooltip details.

Security and Policy Enforcement

TinyFlows enforces OpenHuman’s security model centrally, ensuring workflows cannot escalate privileges beyond the user's tier.

Tool-Level Permissions

Each tool declares required capabilities (read, write, network, install). Before invocation, the engine checks the active SecurityPolicy against the tool's requirements. If the user's tier lacks auto-approval for a capability, the engine pauses and emits an approval_requested event.

Sandboxed Execution

Nodes may run in sandboxed or none mode. Sandboxed tools receive a temporary working directory via the OPENHUMAN_ACTION_DIR environment variable, preventing unintended filesystem access outside the scoped context.

Approval Gates

When a node requires elevated permissions, the UI displays an ApprovalGate dialog. Execution resumes only after explicit user consent, with the decision logged for audit. These checks are centralized in src/openhuman/tools/policy.rs, guaranteeing consistent enforcement across all TinyFlows executions.

Practical Code Examples

Defining a Workflow in JSON

Workflows are stored as JSON files in ~/.openhuman/workspace/flows/:

{
  "id": "example-workflow-1",
  "name": "Fetch and Store Data",
  "nodes": [
    {
      "id": "n1",
      "type": "http_request",
      "args": { "method": "GET", "url": "https://api.example.com/data" }
    },
    {
      "id": "n2",
      "type": "write_file",
      "args": { "path": "output.json", "content_from": "n1" }
    }
  ],
  "edges": [{ "source": "n1", "target": "n2" }]
}

Running a Flow from Rust

Execute workflows programmatically using the Core harness:

use openhuman_core::flows::tinyflows::{self, Workflow};
use openhuman_core::Harness;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Build a Harness with full access
    let harness = Harness::builder()
        .access(openhuman_core::Access::full())
        .build()
        .await?;

    // Load a workflow from the workspace
    let wf: Workflow = harness
        .load_workflow("example-workflow-1")
        .await?;

    // Execute it
    let result = tinyflows::engine::run_workflow(&harness, wf).await?;
    println!("Workflow finished: {:?}", result);
    Ok(())
}

Updating a Flow from the UI

TypeScript clients can mutate workflows using JSON Patch operations:

import { coreRpcClient } from '@/services/coreRpcClient';

async function renameNode(flowId: string, nodeId: string, newName: string) {
  const patch = {
    op: 'replace',
    path: `/nodes/${nodeId}/name`,
    value: newName,
  };
  await coreRpcClient.applyPatch(flowId, [patch]);
}

Summary

  • TinyFlows is the Rust-based workflow engine in src/openhuman/flows/ that executes JSON-defined automation graphs.
  • The visual canvas (app/src/components/FlowCanvas.tsx) provides a React-based interface for building and monitoring workflows, syncing state via JSON-RPC and WebSocket events.
  • Tool integration leverages the existing tinytools registry, allowing any agent tool to function as a workflow node.
  • Security enforcement happens centrally in src/openhuman/tools/policy.rs, respecting sandbox modes, permission tiers, and approval gates.
  • Persistence uses local JSON files in the workspace directory, enabling offline-first workflow authoring and version control.

Frequently Asked Questions

What is TinyFlows in the OpenHuman workflow system?

TinyFlows is the lightweight, JSON-driven orchestration engine implemented in the Core Rust crate. It defines the data structures for nodes and edges in src/openhuman/flows/tinyflows/types.rs and executes graphs via the run_workflow function in engine.rs, enabling users to chain together agent tools, scripts, and conditional logic.

How does the visual canvas communicate with the Rust Core?

The React canvas uses coreRpcClient (defined in app/src/lib/flow/flowService.ts) to send JSON-RPC requests such as getFlow, applyPatch, and run_flow to the Core. Real-time execution state flows back through WebSocket events like workflow_progress, allowing the UI to highlight running, completed, and failed nodes without polling.

Can I use any OpenHuman tool inside a TinyFlows workflow?

Yes. TinyFlows integrates with the agent tool ecosystem through all_tinyflows_agent_tools() in src/openhuman/tools/ops.rs. This means any tool implementing the Tool trait—including custom HTTP requests, Rhai scripts, or file operations—can be added as a node type and referenced in the workflow JSON.

Where are workflow definitions stored on disk?

Workflows are persisted as JSON files in .openhuman/workspace/flows/ within the user's home directory. Each workflow has a unique ID that maps to a file (e.g., example-workflow-1.json), making them portable, version-controllable, and editable both through the canvas and by hand.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →