# How the Tauri Backend Is Wired in LLM Wiki: React-to-Rust Communication Explained

> Discover how the Tauri backend integrates with the LLM Wiki React frontend. Learn about secure Rust communication, invoke calls, and event listeners for a seamless web UI.

- Repository: [nash_su/llm_wiki](https://github.com/nashsu/llm_wiki)
- Tags: internals
- Published: 2026-09-13

---

**The LLM Wiki Tauri backend uses a two-way communication layer where the React frontend calls Rust functions via `invoke` and receives streaming data via event listeners, enabling secure file system access and subprocess management from a web-based UI.**

LLM Wiki is an open-source desktop application by `nashsu` that combines a **Vite-powered React frontend** with a **Rust backend** using the Tauri framework. Understanding how the Tauri backend is wired reveals a clean architecture where the UI handles state and rendering while native capabilities—like spawning LLM subprocesses or reading local files—run in sandboxed Rust commands. This wiring is implemented through the `@tauri-apps/api` package, which exposes two core primitives: `invoke` for RPC-style requests and `listen`/`emit` for event-driven streaming.

## Frontend-to-Backend Communication Primitives

The connection between the TypeScript frontend and the Rust backend relies on two distinct patterns depending on whether the operation is request-response or streaming.

### The Invoke Pattern for RPC Calls

For discrete operations like searching files or extracting images, the frontend uses `invoke` from `@tauri-apps/api/core`. This function sends a serialized payload to a Rust function marked with `#[tauri::command]` and returns a Promise that resolves with the Rust return value.

In [`src/lib/anytxt-search.ts`](https://github.com/nashsu/llm_wiki/blob/main/src/lib/anytxt-search.ts), the frontend requests a search from the native AnyTXT indexer:

```typescript
import { invoke } from "@tauri-apps/api/core"

export async function searchAnytxt(query: string) {
  // Sends the request to the Rust command `anytxt_search`
  const results = await invoke<Record<string, any>>("anytxt_search", { query })
  return results
}

```

Similarly, [`src/lib/extract-source-images.ts`](https://github.com/nashsu/llm_wiki/blob/main/src/lib/extract-source-images.ts) calls `invoke("extract_images", {...})` to process local files, and [`src/lib/markdown-image-resolver.ts`](https://github.com/nashsu/llm_wiki/blob/main/src/lib/markdown-image-resolver.ts) uses `invoke("convert_file_src", {...})` to resolve secure URLs for local assets.

### Event-Driven Streaming with Listen/Emit

For long-running operations like streaming tokens from a Codex CLI process, the frontend cannot await a single response. Instead, it subscribes to events using `listen` from `@tauri-apps/api/event` while the Rust backend emits tokens incrementally.

The [`src/lib/codex-cli-transport.ts`](https://github.com/nashsu/llm_wiki/blob/main/src/lib/codex-cli-transport.ts) file demonstrates this pattern:

```typescript
import { invoke } from "@tauri-apps/api/core"
import { listen, type UnlistenFn } from "@tauri-apps/api/event"

export async function streamCodexCli(config, messages, callbacks) {
  const streamId = crypto.randomUUID()

  // Listen for token events emitted by the Rust command
  const unlistenData = await listen<string>(`codex-cli:${streamId}`, ev => {
    const token = parseCodexCliLine(ev.payload)
    token !== null ? callbacks.onToken(token) : captureUnparsed(ev.payload)
  })

  // Ask the Rust side to spawn the Codex CLI subprocess
  await invoke("codex_cli_spawn", {
    streamId,
    model: config.model,
    prompt: buildPrompt(messages),
  })
}

```

The Rust side emits events to the specific `streamId` channel, allowing multiple concurrent streams without collision.

## Backend Command Implementation in Rust

All native functionality lives in `src-tauri/src/commands/` as asynchronous functions annotated with `#[tauri::command]`. These commands receive the `AppHandle` for emitting events and return `Result<T, String>` for error handling.

### Command Registration in lib.rs

Before a Rust function is callable from the frontend, it must be registered in the Tauri builder. The [`src-tauri/src/lib.rs`](https://github.com/nashsu/llm_wiki/blob/main/src-tauri/src/lib.rs) file (or [`main.rs`](https://github.com/nashsu/llm_wiki/blob/main/main.rs) in some configurations) collects all commands into the invoke handler:

```rust
fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            codex_cli_spawn,
            codex_cli_kill,
            anytxt_search,
            extract_images,
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

```

This registration step is critical; any command omitted from the `generate_handler!` macro will be unreachable from the frontend.

### Streaming Events from Rust to Frontend

Command functions that handle long-running tasks use `app.emit_all` to push data to the frontend without returning immediately. The [`src-tauri/src/commands/codex_cli.rs`](https://github.com/nashsu/llm_wiki/blob/main/src-tauri/src/commands/codex_cli.rs) file implements this for the Codex CLI integration:

```rust
#[tauri::command]
async fn codex_cli_spawn(
    app: tauri::AppHandle,
    payload: SpawnPayload,
) -> Result<(), String> {
    // Spawn the `codex exec --json` subprocess, pipe stdout/stderr
    // For every JSONL line that contains an `agent_message`,
    // emit an event that the front-end is listening for:
    app.emit_all(
        &format!("codex-cli:{}", payload.stream_id),
        token_text,
    )?;
    // When the process finishes, emit a “done” event:
    app.emit_all(
        &format!("codex-cli:{}:done", payload.stream_id),
        DonePayload { code, stdout, stderr },
    )?;
    Ok(())
}

```

When the user aborts a request, the frontend calls a separate command like `codex_cli_kill` to terminate the subprocess, demonstrating how stateful operations are managed across the boundary.

## Configuration and Bootstrapping

### Tauri Configuration (tauri.conf.json)

The [`src-tauri/tauri.conf.json`](https://github.com/nashsu/llm_wiki/blob/main/src-tauri/tauri.conf.json) file stitches the frontend and backend together. It points `frontendDist` to the compiled Vite output (`../dist`), defines the main window configuration, and sets security policies like the Content Security Policy (CSP) and `assetProtocol` for accessing local files. This configuration ensures the React UI mounts correctly inside the WebView while maintaining strict permissions for native API access.

## Real-World Implementation Examples

### Streaming LLM Output (codex-cli-transport.ts)

The most complex wiring in LLM Wiki handles real-time LLM token streaming. The frontend generates a UUID, starts listening on that channel, then invokes the spawn command. As Rust emits tokens, they flow through the event listener:

```typescript
// From src/lib/codex-cli-transport.ts
const unlisten = await listen<string>("codex-cli:12345", ev => {
  console.log("token:", ev.payload)   // token emitted from Rust side
})
// Cleanup when done
unlisten()

```

### File System Operations

For simpler file operations, the pattern remains consistent. The frontend invokes a command with file paths, and Rust performs the IO. The [`src/lib/markdown-image-resolver.ts`](https://github.com/nashsu/llm_wiki/blob/main/src/lib/markdown-image-resolver.ts) utility uses `convertFileSrc` from Tauri’s API to transform local file paths into secure asset URLs that the WebView can render, bypassing standard browser security restrictions on local file access.

## Summary

- **RPC via `invoke`**: The frontend calls discrete Rust commands using `invoke` from `@tauri-apps/api/core`, with commands registered in [`src-tauri/src/lib.rs`](https://github.com/nashsu/llm_wiki/blob/main/src-tauri/src/lib.rs) via `generate_handler!`.
- **Streaming via `listen`/`emit`**: Long-running tasks emit events from Rust using `app.emit_all`, which the frontend receives through `listen` channels identified by unique stream IDs.
- **Command Location**: All backend logic lives in `src-tauri/src/commands/*.rs`, with each function marked by `#[tauri::command]`.
- **Configuration**: [`src-tauri/tauri.conf.json`](https://github.com/nashsu/llm_wiki/blob/main/src-tauri/tauri.conf.json) binds the Vite frontend to the Rust binary, defining security policies and the application window.
- **Type Safety**: The TypeScript definitions in the frontend match the Rust function signatures, ensuring compile-time correctness across the language boundary.

## Frequently Asked Questions

### How does LLM Wiki stream tokens from the Rust backend to the React UI?

LLM Wiki uses Tauri’s event system. The frontend generates a unique `streamId`, calls `listen` on the channel `codex-cli:${streamId}`, and then invokes `codex_cli_spawn`. The Rust backend emits tokens incrementally using `app.emit_all` with the matching channel name, allowing real-time updates without polling.

### What is the difference between `invoke` and `listen` in the Tauri backend?

`invoke` is a request-response RPC call where the frontend sends data to a Rust command and awaits a return value, suitable for discrete operations like file searches. `listen` is a subscription mechanism where the frontend registers a callback for events emitted asynchronously by Rust, essential for streaming data or progress updates that occur over time.

### Where are the Rust commands registered in the LLM Wiki codebase?

Commands are registered in [`src-tauri/src/lib.rs`](https://github.com/nashsu/llm_wiki/blob/main/src-tauri/src/lib.rs) (or [`main.rs`](https://github.com/nashsu/llm_wiki/blob/main/main.rs)) inside the `tauri::Builder` chain using `invoke_handler(tauri::generate_handler![...])`. Only functions listed in this macro—such as `codex_cli_spawn` and `anytxt_search`—are exposed to the frontend.

### How does the frontend call native file system operations in LLM Wiki?

The frontend calls `invoke` with commands like `"extract_images"` or `"convert_file_src"`, passing file paths as arguments. The corresponding Rust commands in `src-tauri/src/commands/` perform the actual IO operations using standard Rust libraries, then return results (or emit events) back to the TypeScript layer.