# How Tauri JSON-RPC Bridges Rust Core and React Frontend in OpenHuman

> Discover how OpenHuman leverages Tauri's JSON-RPC for type-safe communication between Rust and React. Learn about authentication and endpoint discovery via Tauri IPC.

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

---

**OpenHuman uses JSON-RPC 2.0 over a local HTTP endpoint to enable type-safe communication between the Rust core binary and the React/Vite frontend, with Tauri IPC handling authentication and endpoint discovery.**

OpenHuman is an open-source AI interface that separates business logic from UI concerns using a Rust core and React frontend. The architecture implements **Tauri JSON-RPC** to create a language-agnostic bridge, allowing the core to run as a standalone binary while the React layer communicates through a well-defined RPC surface. This design ensures the core remains testable independently of the desktop shell.

## Transport Layer: The HTTP JSON-RPC Server

The Rust core exposes its functionality via a lightweight HTTP server defined in [`src/core/jsonrpc.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc.rs). When the core starts, it binds to a random localhost port and listens for standard JSON-RPC 2.0 requests.

The server expects envelopes following the `{jsonrpc:"2.0", id, method, params}` format and routes each request to the appropriate domain controller through `crate::core::jsonrpc::invoke_method`. This transport layer is deliberately kept thin; domain logic resides in separate modules under `src/openhuman/*/schemas`, ensuring the JSON-RPC implementation remains purely concerned with request routing and serialization.

## Endpoint Discovery: Tauri IPC Commands

Since the core runs as a separate process with a dynamic port, the Tauri layer must broker the connection details. In [`app/src-tauri/src/core_process.rs`](https://github.com/tinyhumansai/openhuman/blob/main/app/src-tauri/src/core_process.rs), the desktop application launches the core binary and captures the RPC URL (e.g., `http://127.0.0.1:57123/rpc`) along with a bearer token stored in `OPENHUMAN_CORE_TOKEN`.

These values are exposed to the JavaScript side through dedicated Tauri commands defined in [`app/src-tauri/src/core_rpc.rs`](https://github.com/tinyhumansai/openhuman/blob/main/app/src-tauri/src/core_rpc.rs):

```rust
#[tauri::command]
fn core_rpc_url() -> String { … }

#[tauri::command]
fn core_rpc_token() -> Option<String> { … }

```

The frontend invokes these commands to retrieve the endpoint address and authentication token before making any RPC calls.

## Frontend Implementation: The TypeScript RPC Client

The React frontend consumes the core API through a centralized client in [`app/src/services/coreRpcClient.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/src/services/coreRpcClient.ts). The `callCoreRpc` function orchestrates the full request lifecycle:

1. Retrieves the endpoint URL and bearer token via Tauri IPC
2. Constructs a JSON-RPC 2.0 payload
3. Issues an authenticated `fetch` request
4. Normalizes responses, converting JSON-RPC errors into JavaScript exceptions

```typescript
export async function callCoreRpc<T>(method: string, params?: unknown): Promise<T> {
  const url = await invoke('core_rpc_url');
  const token = await invoke('core_rpc_token');
  const body = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params });
  const resp = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
    body,
  });
  const { result, error } = await resp.json();
  if (error) throw new CoreRpcError(error);
  return result as T;
}

```

This abstraction ensures all network concerns are handled in one location, providing consistent error handling and authentication for every core interaction.

## Developer Experience: Typed Command Wrappers

To maintain type safety and simplify domain logic access, OpenHuman provides thin wrapper modules under `app/src/utils/tauriCommands/`. Files like [`config.ts`](https://github.com/tinyhumansai/openhuman/blob/main/config.ts) and [`auth.ts`](https://github.com/tinyhumansai/openhuman/blob/main/auth.ts) import `callCoreRpc` and expose strongly-typed functions that forward method names and parameters to the core.

For example, configuration operations are wrapped in [`app/src/utils/tauriCommands/config.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/src/utils/tauriCommands/config.ts), allowing UI components to call domain-specific methods without constructing raw JSON-RPC payloads. This pattern keeps the React code clean while ensuring the TypeScript types match the Rust schema definitions.

## Boot Sequence: Initializing the Connection

The connection between React and Rust follows a strict initialization sequence:

1. **Core Launch**: [`app/src-tauri/src/core_process.rs`](https://github.com/tinyhumansai/openhuman/blob/main/app/src-tauri/src/core_process.rs) spawns the core binary, often with flags like `--jsonrpc-only`
2. **Endpoint Capture**: The core prints its RPC URL to stdout, which Tauri captures via `CoreProcessHandle`
3. **UI Hydration**: The React app loads and calls `core_rpc_url()` and `core_rpc_token()` to cache connection details
4. **Runtime Communication**: UI components invoke `tauriCommand` wrappers, which translate to authenticated JSON-RPC requests against the core

This sequence ensures the frontend never attempts to connect before the core is ready and authenticated.

## Why JSON-RPC?

OpenHuman chose JSON-RPC 2.0 over Tauri's native IPC for several architectural reasons:

- **Language Agnosticism**: The same RPC definitions serve the Rust core, CLI tools, and integration tests without modification
- **Structured Error Handling**: JSON-RPC's standardized `error` object maps cleanly to Rust's `RpcOutcome` and JavaScript exceptions, providing consistent error propagation across the boundary
- **Extensibility**: Adding new capabilities requires only a schema file in the Rust core and a corresponding TypeScript wrapper; the transport layer remains unchanged

## Summary

- **Transport**: [`src/core/jsonrpc.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/jsonrpc.rs) runs an HTTP server speaking JSON-RPC 2.0 on a random localhost port
- **Discovery**: Tauri commands in [`app/src-tauri/src/core_rpc.rs`](https://github.com/tinyhumansai/openhuman/blob/main/app/src-tauri/src/core_rpc.rs) expose the endpoint URL and bearer token to the frontend
- **Client**: [`app/src/services/coreRpcClient.ts`](https://github.com/tinyhumansai/openhuman/blob/main/app/src/services/coreRpcClient.ts) handles authenticated `fetch` requests and error normalization
- **Wrappers**: `app/src/utils/tauriCommands/*.ts` provide type-safe access to core domain logic
- **Security**: Bearer token authentication (`OPENHUMAN_CORE_TOKEN`) prevents unauthorized local access to the core API

## Frequently Asked Questions

### What protocol does OpenHuman use for Rust-to-React communication?

OpenHuman uses **JSON-RPC 2.0** over HTTP. The Rust core implements a local HTTP server that accepts standard JSON-RPC envelopes, while the React frontend sends authenticated POST requests via `fetch`. This approach provides a language-agnostic interface that works identically whether called from the desktop UI, CLI tools, or automated tests.

### How does the frontend authenticate with the Rust core?

Authentication uses a bearer token generated by the core during startup and stored in the `OPENHUMAN_CORE_TOKEN` environment variable. The Tauri layer exposes this token via the `core_rpc_token()` command, and the TypeScript client includes it in the `Authorization: Bearer ...` header of every JSON-RPC request. This ensures only the authorized Tauri process can invoke core methods.

### Why use JSON-RPC instead of Tauri's native command system?

While Tauri provides direct Rust-to-JavaScript commands, OpenHuman uses JSON-RPC to maintain a strict separation between the UI shell and business logic. The JSON-RPC approach allows the core to run as a standalone binary (enabling CLI usage and headless testing) while keeping the React frontend decoupled from Tauri-specific APIs. It also provides standardized error handling and makes the API discoverable by any HTTP client.

### Where is the RPC endpoint configured during startup?

The endpoint is dynamically assigned. When [`app/src-tauri/src/core_process.rs`](https://github.com/tinyhumansai/openhuman/blob/main/app/src-tauri/src/core_process.rs) launches the core, the core binds to a random available port and prints the URL. Tauri captures this output and stores the URL, making it available via the `core_rpc_url()` command. The React app retrieves this value at runtime rather than using a hardcoded address, preventing port conflicts on different machines.