# How OpenHuman's Rust Core Communicates with the React Frontend Using In-Process JSON-RPC

> Discover how OpenHuman's Rust core communicates with its React frontend using in-process JSON-RPC. Learn about Tauri v2, HTTP servers, and the Tauri invoke API.

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

---

**OpenHuman uses Tauri v2 to host a React UI that communicates with its Rust core via an embedded HTTP server exposing JSON-RPC 2.0 endpoints, bridged through Tauri's invoke API.**

The **OpenHuman** desktop client orchestrates a React frontend and Rust core within a single process using a hybrid transport layer. According to the source code in `tinyhumansai/openhuman`, the architecture employs **Tauri's** native bridge to forward requests to an in-process HTTP server that implements the **JSON-RPC 2.0** protocol.

## Architecture Overview

The communication stack decouples the UI from core logic while maintaining the performance benefits of in-process execution. The pipeline consists of four distinct layers that transform JavaScript calls into native Rust method invocations.

**Key Components:**
- **Frontend Client** ([`app/src/lib/coreRpcClient.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/src/lib/coreRpcClient.ts)) – Wraps Tauri's invoke API to build JSON-RPC payloads
- **Tauri Bridge** ([`app/src-tauri/src/core_rpc.rs`](https://github.com/tinyhumansai/openhuman/blob/main/app/src-tauri/src/core_rpc.rs)) – Implements the `relay_http_rpc` command that forwards HTTP requests
- **Embedded Server** ([`src/rpc/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/rpc/mod.rs)) – Hyper-based HTTP listener handling the `/rpc` endpoint
- **Dispatcher** ([`src/rpc/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/rpc/mod.rs)) – Routes parsed `RpcRequest` structs to domain controllers (e.g., `openhuman.agent_turn`, `openhuman.memory_*`)

## The Communication Pipeline

### Frontend Layer: Building the JSON-RPC Request

The React application never calls Rust functions directly. Instead, it uses the `coreRpcClient` utility to construct standards-compliant JSON-RPC 2.0 messages. This client injects the method name, parameters, and a unique identifier into a payload that travels through Tauri's secure IPC boundary.

```typescript
// app/src/lib/coreRpcClient.ts
export async function callRpc<T>(method: string, params: any = {}): Promise<T> {
  const rpcUrl = await getRpcUrl(); // Injected by core at startup
  const payload = { 
    jsonrpc: '2.0', 
    id: Date.now(), 
    method, 
    params 
  };
  
  const response = await window.__TAURI__.invoke('core_rpc', {
    method: 'relay_http_rpc',
    url: rpcUrl,
    body: JSON.stringify(payload),
  });
  
  const { result, error } = JSON.parse(response);
  if (error) throw new Error(error.message);
  return result as T;
}

```

### Tauri Bridge: The relay_http_rpc Command

The Tauri sidecar implements a single command, `relay_http_rpc`, defined in [`app/src-tauri/src/core_rpc.rs`](https://github.com/tinyhumansai/openhuman/blob/main/app/src-tauri/src/core_rpc.rs). This function receives the JSON string from the frontend, performs an asynchronous HTTP POST to the core's embedded server, and returns the raw response body to the renderer.

```rust
// app/src-tauri/src/core_rpc.rs
pub(crate) async fn relay_http_rpc(url: String, body: String) -> Result<String, String> {
    // Forwards the payload to the in-process HTTP server
    post_json_rpc(&url, &body).await
}

```

### Core HTTP Server: Hyper-Powered JSON-RPC

When the application initializes (`CoreBuilder::build`), the Rust core spawns an **in-process HTTP server** on a random available port (e.g., `http://127.0.0.1:<port>/rpc`). The server uses the `hyper` crate to handle incoming connections.

```rust
// src/rpc/mod.rs
pub async fn serve(bind_addr: SocketAddr, dispatcher: RpcDispatcher) -> Result<()> {
    let make_svc = make_service_fn(move |_| {
        let disp = dispatcher.clone();
        async move {
            Ok::<_, hyper::Error>(service_fn(move |req| {
                let d = disp.clone();
                async move { d.handle(req).await }
            }))
        }
    });
    hyper::Server::bind(&bind_addr).serve(make_svc).await?;
    Ok(())
}

```

### Dispatcher and Request Routing

The `RpcDispatcher` deserializes incoming bytes into `RpcRequest` structs, routes them to the appropriate controller (registered in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs)), and wraps the result in a `RpcResponse`. Errors are encoded using the `StructuredError` type defined in [`src/rpc/structured_error.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/rpc/structured_error.rs).

```rust
// src/rpc/mod.rs (excerpt)
impl RpcDispatcher {
    async fn handle(&self, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
        let rpc_req: RpcRequest = hyper::body::to_bytes(req.into_body()).await?.into();
        let result = self.route(rpc_req.method, rpc_req.params).await;
        let resp = RpcResponse::new(rpc_req.id, result);
        Ok(Response::new(Body::from(serde_json::to_string(&resp)?)))
    }
}

```

## Error Handling with StructuredError

The core standardizes error responses using the `StructuredError` struct to ensure consistent error codes and messages across all RPC methods. This guarantees that the React frontend can reliably parse failure states regardless of which domain controller triggered the error.

```rust
// src/rpc/structured_error.rs
#[derive(Serialize)]
pub struct StructuredError {
    code: i32,
    message: String,
    data: Option<serde_json::Value>,
}

```

## Why Use an In-Process HTTP Server?

**Uniformity**: CLI tools, test harnesses, and the desktop UI all consume the same HTTP-JSON-RPC interface, eliminating transport-specific logic duplication.

**Isolation**: The JSON-RPC layer enforces request validation, authentication, and per-method permission checks before delegating to business logic in [`src/openhuman/agent/dispatcher.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/dispatcher.rs).

**Extensibility**: New domains (such as `documents` or `web3` namespaces) register controllers under the existing `/rpc` endpoint without modifying frontend transport code.

## Key Source Files

- **[`app/src-tauri/src/core_rpc.rs`](https://github.com/tinyhumansai/openhuman/blob/main/app/src-tauri/src/core_rpc.rs)** – Implements the `relay_http_rpc` Tauri command that bridges the renderer to the core's HTTP server
- **[`app/src/lib/coreRpcClient.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/src/lib/coreRpcClient.ts)** – TypeScript wrapper used throughout the UI to build JSON-RPC payloads and invoke the Tauri command
- **[`src/rpc/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/rpc/mod.rs)** – Defines the JSON-RPC server, `RpcDispatcher`, and the `serve` function that registers the `/rpc` handler
- **[`src/rpc/structured_error.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/rpc/structured_error.rs)** – Standard JSON-RPC error format ensuring consistent error handling across domains
- **[`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs)** – Registration site for all domain controllers (e.g., `openhuman.profile_get`, `openhuman.memory_*`)

## Summary

- OpenHuman uses **Tauri v2** to embed a React frontend and Rust core in a single process
- Communication flows through **`coreRpcClient`** → **`relay_http_rpc`** → **in-process HTTP server** → **`RpcDispatcher`**
- The embedded server listens on a local port and exposes a **`/rpc`** endpoint powered by `hyper`
- **JSON-RPC 2.0** provides the messaging protocol, with errors standardized via `StructuredError`
- This architecture allows the UI to remain agnostic of Rust implementation details while supporting multiple client types (CLI, tests, desktop)

## Frequently Asked Questions

### Why does OpenHuman use an HTTP server instead of standard Tauri invoke?

The in-process HTTP server provides a unified interface for all clients. While Tauri's `invoke` API handles the frontend-to-Rust bridge, the HTTP layer allows the same JSON-RPC endpoints to serve CLI tools, automated tests, and potential future headless clients without code duplication. This design also enables the core to enforce consistent authentication and validation logic at the HTTP boundary.

### How does the frontend discover the RPC server's port?

The Rust core generates a random available port during initialization (`CoreBuilder::build`) and injects the complete URL (e.g., `http://127.0.0.1:49251/rpc`) into the frontend context before the React application mounts. The `getRpcUrl()` helper in [`coreRpcClient.ts`](https://github.com/tinyhumansai/openhuman/blob/main/coreRpcClient.ts) retrieves this injected configuration, ensuring the client always targets the correct in-process server instance.

### What JSON-RPC version does OpenHuman implement?

The codebase implements **JSON-RPC 2.0** as evidenced by the request structure containing `jsonrpc: "2.0"`, `method`, `params`, and `id` fields. The dispatcher in [`src/rpc/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/rpc/mod.rs) parses these fields into `RpcRequest` structs and returns `RpcResponse` objects that strictly follow the protocol specification, including proper error objects with `code`, `message`, and optional `data` fields.

### How are methods routed to their implementations?

The `RpcDispatcher` maintains a registry of available controllers populated during core initialization in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs). When a request arrives at the `/rpc` endpoint, the dispatcher matches the `method` string (e.g., `openhuman.agent_turn`) against registered handlers and invokes the corresponding Rust function with the deserialized parameters. Agent-related methods delegate to the dispatcher defined in [`src/openhuman/agent/dispatcher.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/agent/dispatcher.rs) for tool execution coordination.