How OpenHuman's Rust Core and React/Tauri Shell Architecture Works Together

OpenHuman pairs a Rust-based core library handling all business logic with a React frontend embedded in a Tauri desktop shell, communicating via an in-memory JSON-RPC channel secured by a per-session bearer token.

OpenHuman is an AI agent framework developed by tinyhumansai that separates high-performance backend operations from the user interface through a distinct two-layer architecture. The system centers on a portable Rust core that manages agents, memory, and tool execution, while the Tauri-based desktop application provides a React/TypeScript frontend. This design enables the core to run in-process as a background Tokio task, eliminating network latency while maintaining strict security boundaries.

Core Startup Inside the Tauri Shell

When the desktop application launches, Tauri executes the start_core_process command defined in app/src-tauri/src/core_process.rs. This command constructs the core using CoreBuilder, configures the appropriate domain and service sets, and spawns the runtime as a Tokio task within the same process.

// app/src-tauri/src/core_process.rs (simplified)
let core = CoreBuilder::new()
    .domains(DomainSet::full())
    .services(ServiceSet::desktop())
    .build()
    .await?;
let handle = CoreProcessHandle::new(core);
handle.start().await?;

Upon initialization, the core binds to a random localhost port and generates a hex-encoded bearer token. The Tauri command core_rpc_token passes this token to the React UI, ensuring the credential lives only in process memory and never touches the filesystem.

JSON-RPC Transport Layer

The Rust core exposes its public API through modules located in src/openhuman/web_chat, which register JSON-RPC controllers following the openhuman.web_chat_* naming convention. The file src/core/all.rs automatically discovers these controllers, while src/core/jsonrpc.rs handles request routing through a generic dispatcher.

The transport layer implements a lightweight HTTP server that authenticates requests via the Authorization: Bearer <token> header. This in-memory token validation ensures that only the local UI instance can invoke core methods during the application lifecycle.

React UI Integration and RPC Client

The React frontend communicates with the core through the coreRpcClient service located at app/src/services/coreRpcClient.ts. This client constructs requests to http://127.0.0.1:<port>/rpc, injects the bearer token retrieved from the Tauri backend, and serializes method calls according to the JSON-RPC 2.0 specification.

// app/src/services/coreRpcClient.ts (simplified)
export async function invokeRpc<T>(method: string, params: unknown) {
  const token = await invoke<string>('core_rpc_token', {});
  const resp = await fetch(`http://127.0.0.1:${port}/rpc`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params }),
  });
  const { result } = await resp.json();
  return result as T;
}

The application bootstrap in app/src/App.tsx establishes a provider chain that exposes this client throughout the component tree:


Sentry.ErrorBoundary → Redux Provider → PersistGate → CoreStateProvider → 
SocketProvider → ChatRuntimeProvider → HashRouter → CommandProvider

The CoreStateProvider initializes the connection by invoking coreRpcClient.invokeRpc('openhuman.web_chat_snapshot', …) to synchronize the UI state with the core's current snapshot.

Real-Time Data Flow: Processing a Chat Turn

The architecture handles real-time agent interactions through an event-driven pipeline. When a user submits a message, the data flows through these layers:

  1. UI Action: The React component calls coreRpcClient.invokeRpc('openhuman.web_chat_turn', { prompt }).

  2. Core Processing: The request reaches src/openhuman/web_chat/ops.rs, which instantiates a Harness (defined in src/openhuman/medulla/contract.rs) to execute the agent turn.

  3. Event Streaming: As the agent processes the turn, it generates HarnessEvent payloads representing tool calls, tool results, and message chunks. These stream back to the UI through the persistent RPC connection.

  4. State Synchronization: The ChatRuntimeProvider consumes these events, dispatches updates to the Redux store, and triggers re-renders of the conversation interface.

Security Model and Process Isolation

OpenHuman implements multiple security layers to protect against unauthorized tool execution and data exfiltration.

Per-Session Authentication: The bearer token generated at startup in core_process.rs acts as a capability token. Any process lacking this specific token cannot communicate with the core's JSON-RPC endpoint, effectively sandboxing the API within the single application instance.

Approval Gates: The core enforces user confirmation for sensitive operations through the mechanism defined in src/openhuman/security/approval.rs. This gate can pause tool execution mid-harness until the UI receives explicit user consent.

Tool Policy Enforcement: All I/O operations route through src/openhuman/tools/policy.rs, which evaluates permissions against the current execution tier and enforces sandbox limits, timeouts, and resource constraints.

// Rust: launching the core from the Tauri command (core_process.rs)
#[tauri::command]
pub async fn start_core_process() -> Result<(), String> {
    let core = CoreBuilder::new()
        .domains(DomainSet::full())
        .services(ServiceSet::desktop())
        .build()
        .await
        .map_err(|e| e.to_string())?;
    CoreProcessHandle::new(core).start().await.map_err(|e| e.to_string())
}
// TypeScript: sending a chat turn from a React component
import { invokeRpc } from '../services/coreRpcClient';

async function sendTurn(prompt: string) {
  const turnResult = await invokeRpc<any>('openhuman.web_chat_turn', { prompt });
  console.log('Turn started', turnResult);
}

Summary

  • In-Process Architecture: The Rust core runs as a Tokio task inside the Tauri process, eliminating network overhead while maintaining logical separation from the React UI.
  • Secure JSON-RPC Channel: Communication occurs over localhost HTTP with a per-session bearer token generated in core_process.rs and validated by the dispatcher in src/core/jsonrpc.rs.
  • Provider Chain Pattern: The React frontend initializes the RPC client through nested providers in App.tsx, making core methods available throughout the component tree via coreRpcClient.ts.
  • Event-Driven Updates: Agent turns produce streaming HarnessEvent payloads from src/openhuman/medulla/contract.rs that update the UI in real-time without polling.
  • Layered Security: The combination of in-memory tokens, approval gates in src/openhuman/security/approval.rs, and tool policies in src/openhuman/tools/policy.rs ensures fine-grained control over agent capabilities.

Frequently Asked Questions

How does the React frontend discover the Rust core's port and authentication token?

When Tauri launches the application, the start_core_process command in app/src-tauri/src/core_process.rs starts the core on a random available port and generates a hex-encoded bearer token. The React UI retrieves this token by invoking the Tauri command core_rpc_token, which returns the in-memory credential needed to authenticate JSON-RPC requests to the local HTTP endpoint.

Can the Rust core run independently of the Tauri desktop application?

Yes. The core is designed as a portable library that can be embedded in multiple contexts. While the desktop shell uses CoreBuilder with ServiceSet::desktop() to run in-process, the same core can operate in CLI mode, Docker containers, or remote services by configuring different service sets. The JSON-RPC interface remains consistent regardless of the host environment.

What prevents unauthorized applications from accessing the core's JSON-RPC endpoint?

Security relies on the per-session bearer token that exists only in the memory of the running process. Since the token is randomly generated at startup and never written to disk or exposed through environment variables, external processes cannot authenticate requests to http://127.0.0.1:<port>/rpc. Additionally, the core binds exclusively to localhost, refusing external network connections.

How does the system handle long-running agent operations without blocking the UI?

The Rust core processes agent turns asynchronously using Tokio tasks defined in src/openhuman/web_chat/ops.rs. Rather than waiting for the entire turn to complete, the core streams HarnessEvent objects back to the React client as they occur. The ChatRuntimeProvider in the UI layer consumes these events incrementally, allowing the interface to render partial results and tool execution status while the background task continues processing.

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 →