How the In-Process Core Communicates with the Tauri Frontend Using JSON-RPC and Bearer Token Authentication in OpenHuman

OpenHuman uses an in-process Rust core that exposes an HTTP JSON-RPC endpoint on a local port, secured by an ephemeral bearer token stored in the OPENHUMAN_CORE_TOKEN environment variable, which the Tauri frontend retrieves via invoke commands and injects into every request header.

OpenHuman is a desktop AI application built with Tauri that keeps its Rust-based core logic running inside the same process as the UI shell. Rather than using standard Tauri commands for all operations, the core launches an embedded HTTP server that accepts JSON-RPC 2.0 requests. The frontend communicates with this server through a secure, token-authenticated relay system that prevents unauthorized local access.

Core Initialization and Token Generation

When the application starts, app/src-tauri/src/core_process.rs spawns the core as a Tokio task using run_server_embedded_with_ready. During startup, the core generates a cryptographically random bearer token and binds it to the process environment.

let token = generate_random_token();
std::env::set_var("OPENHUMAN_CORE_TOKEN", &token);
let (addr, handle) = CoreBuilder::new()
    .provider(...)
    .access(...)
    .backend_url(...)
    .build()
    .await?
    .run_server_embedded_with_ready(rpc_token: Some(token))
    .await?;

The rpc_token parameter ensures the HTTP server validates this specific token on every incoming request. Because the token lives only in memory and never touches disk, it is ephemeral per session.

Tauri Command Layer

The Tauri backend exposes three distinct commands in app/src-tauri/src/core_rpc.rs that allow the renderer to discover the core's endpoint and authenticate itself:

  • core_rpc_url – Returns the full HTTP URL (e.g., http://127.0.0.1:7788/rpc) where the JSON-RPC server is listening.
  • core_rpc_token – Returns the raw bearer token string stored in OPENHUMAN_CORE_TOKEN.
  • core_rpc_endpoint – Returns both values in a single atomic payload, ensuring the URL and token remain consistent even if the core restarts between calls.

These functions are annotated with #[tauri::command] and registered in the Tauri application builder, making them accessible to the frontend via the invoke API.

Relaying Authenticated Requests

For operations that must forward raw JSON-RPC calls, the backend provides relay_http_rpc. This command constructs a reqwest::Client request and passes it through the apply_auth helper before dispatching:

#[tauri::command]
async fn relay_http_rpc(
    method: String,
    params: serde_json::Value,
) -> Result<serde_json::Value, String> {
    let client = http_client();
    let url = core_rpc_url_value();
    let req = client
        .post(&url)
        .json(&json!({ "jsonrpc": "2.0", "id": 1, "method": method, "params": params }))
        .await
        .map_err(|e| e.to_string())?;
    let req = apply_auth(req)?;  // Inject Authorization header
    let resp = req.send().await.map_err(|e| e.to_string())?;
    let json = resp.json().await.map_err(|e| e.to_string())?;
    Ok(json)
}

The apply_auth function retrieves the token from the environment and attaches an Authorization: Bearer <token> header to the outgoing request.

Frontend Implementation

The TypeScript client in app/src/lib/services/coreRpcClient.ts bridges the Tauri invoke layer with standard web fetch logic. It first obtains the endpoint credentials, then constructs a compliant JSON-RPC 2.0 payload:

import { invoke } from '@tauri-apps/api/tauri';

async function getCoreEndpoint() {
  const { url, token } = await invoke('core_rpc_endpoint');
  return { url, token };
}

async function callCore(method: string, params: any) {
  const { url, token } = await getCoreEndpoint();
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`,
    },
    body: JSON.stringify({ 
      jsonrpc: '2.0', 
      id: Date.now(), 
      method, 
      params 
    }),
  });
  return response.json();
}

This pattern keeps the frontend agnostic of the underlying port allocation while enforcing the bearer token on every call.

Complete Request Lifecycle

The full communication flow follows these steps:

  1. Renderer initiates – The frontend calls invoke('core_rpc_endpoint') to retrieve the current URL and token.
  2. Tauri bridges – The Rust command layer reads OPENHUMAN_CORE_TOKEN and the bound socket address, returning them as a JSON object.
  3. Frontend authenticates – The TypeScript client sets Authorization: Bearer <token> and POSTs to the local HTTP endpoint.
  4. Core validates – The server in src/openhuman/api/rest.rs extracts the header, compares it against the startup token, and rejects mismatches with a 401 Unauthorized error.
  5. RPC execution – Upon validation, the core deserializes the JSON-RPC body, routes the method call (e.g., openhuman.chat_send or openhuman.memory_query), serializes the result, and returns it via HTTP.
  6. Response delivery – The fetch promise resolves in the frontend, delivering the JSON-RPC response object containing the result or error.

Security Model

OpenHuman's architecture provides multiple isolation guarantees:

  • Ephemeral credentials – The bearer token is generated per-process launch and exists only in the OPENHUMAN_CORE_TOKEN environment variable, preventing persistence attacks.
  • Localhost binding – The HTTP server binds strictly to 127.0.0.1, rejecting external network interfaces.
  • Token validation – Every request to src/openhuman/api/rest.rs must present the exact token provided at startup; missing or incorrect tokens terminate the connection immediately.
  • In-process isolation – Because the core runs as a Tokio task within the Tauri process, there is no IPC overhead or socket exposure to other applications.

Summary

  • The Rust core initializes in core_process.rs with run_server_embedded_with_ready, storing a random token in OPENHUMAN_CORE_TOKEN.
  • Tauri commands in core_rpc.rs expose core_rpc_endpoint and relay_http_rpc to ferry the URL, token, and JSON-RPC payloads between frontend and core.
  • The apply_auth helper injects the Authorization: Bearer header using reqwest.
  • The TypeScript client in coreRpcClient.ts retrieves credentials via invoke, then performs authenticated HTTP POSTs following JSON-RPC 2.0 specification.
  • The core validates every request against the startup token in src/openhuman/api/rest.rs, ensuring only the same process can execute RPC methods.

Frequently Asked Questions

How does the frontend discover the core's JSON-RPC port?

The frontend calls the Tauri command core_rpc_endpoint defined in app/src-tauri/src/core_rpc.rs. This command returns both the dynamically allocated localhost URL and the bearer token, ensuring the frontend always targets the correct server instance even after restarts.

What prevents other applications from calling the core's HTTP endpoint?

The core validates a bearer token on every request. This token is generated at startup in core_process.rs, stored in the OPENHUMAN_CORE_TOKEN environment variable, and never written to disk. Because the token is unpredictable and ephemeral per session, external processes cannot authenticate even if they detect the open port.

Why use HTTP JSON-RPC instead of standard Tauri commands?

HTTP JSON-RPC allows the core to expose a standardized API surface that can be consumed by both the Tauri frontend and potential future local clients, while maintaining strict authentication via bearer tokens. The in-process Tokio task architecture keeps latency minimal compared to external process communication.

Where is the bearer token actually validated?

The validation logic resides in src/openhuman/api/rest.rs, where incoming HTTP requests are inspected for the Authorization: Bearer header. The server compares the provided token against the value passed to run_server_embedded_with_ready during initialization, rejecting any mismatches before routing to the JSON-RPC handler.

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 →