# OpenHuman Workflow System: TinyFlows and Visual Canvas Explained

> Explore the OpenHuman workflow system with TinyFlows and its visual canvas. Build, execute, and manage automations securely and efficiently using this Rust-powered orchestration layer.

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

---

**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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/tinyflows/ops.rs)) handles the actual filesystem interaction.

### Visual Canvas Frontend

The React frontend ([`app/src/components/FlowCanvas.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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/`:

```json
{
  "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:

```rust
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:

```typescript
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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/flows/tinyflows/types.rs) and executes graphs via the `run_workflow` function in [`engine.rs`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/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`](https://github.com/tinyhumansai/openhuman/blob/main/example-workflow-1.json)), making them portable, version-controllable, and editable both through the canvas and by hand.