# Architecture of Terax AI: Inside the Two-Process Rust and React Model

> Explore the Terax AI architecture: a secure two-process model with a Rust backend for OS operations and a sandboxed React frontend using Tauri IPC for efficient resource management and robust security.

- Repository: [Crynta/terax-ai](https://github.com/crynta/terax-ai)
- Tags: architecture
- Published: 2026-07-06

---

**Terax AI implements a strict two-process architecture where a privileged Rust backend manages all OS-level operations while a sandboxed React webview frontend communicates exclusively through Tauri’s IPC command layer, enforcing security boundaries and efficient resource pooling.**

Terax AI, an open-source AI-augmented terminal application developed by crynta, separates system privileges from user interface concerns through a Tauri-based design. The architecture of Terax AI relies on a Rust core that owns PTY sessions, filesystem access, Git operations, and AI tool execution, while a Chromium-embedded React frontend renders the UI and delegates every privileged action via a tightly controlled invoke mechanism. This architectural split ensures that AI-generated commands and web content never directly interact with the host operating system.

## Two-Process Model and IPC Architecture

The foundation of Terax AI’s design is a clean separation between the **Rust backend** and the **React frontend**, connected through Tauri’s inter-process communication (IPC) bridge.

### Rust Backend and Command Registry

The backend resides in `src-tauri/` and contains all modules that interact with the operating system. According to the crynta/terax-ai source code, [`src-tauri/src/lib.rs`](https://github.com/crynta/terax-ai/blob/main/src-tauri/src/lib.rs) registers every callable function using `tauri::generate_handler![...]`, exposing specific commands to the webview. Each subsystem—PTY, filesystem, Git, shell, and network—lives in its own module under `src-tauri/src/modules/`, exposing async functions annotated with `#[tauri::command]`.

Key commands registered in the handler include:
- **PTY operations**: `pty_open`, `pty_write`, `pty_resize`, `pty_close`, `pty_has_foreground_process`
- **Filesystem**: `fs_read_file`, `fs_write_file`, `fs_stat`, `fs_search`
- **Git**: `git_status`, `git_diff`, `git_commit`, `git_push`
- **Shell**: `shell_run_command`, `shell_session_*`, `shell_bg_*`
- **Network**: `ai_http_request`, `ai_http_stream` (with SSRF protection)

### React Frontend and IPC Boundaries

The frontend in `src/` is a TypeScript React application that never directly accesses the filesystem or spawns processes. Instead, it invokes backend commands through `window.__TAURI__.invoke`. This creates a strict capability boundary where the UI can only perform actions explicitly whitelisted in the Rust command registry.

## Security Architecture

All inbound data—including terminal escape sequences, file contents, and AI-generated tool arguments—is parsed and validated inside the Rust backend before any privileged operation executes.

### Core Security Mechanisms

The security model implements multiple defensive layers:
- **Deny-list validation**: Unsafe terminal escape sequences are filtered through [`src-tauri/src/security.ts`](https://github.com/crynta/terax-ai/blob/main/src-tauri/src/security.ts) before reaching the PTY.
- **Workspace authorization**: A registry restricts filesystem and shell access to directories explicitly authorized by the user.
- **SSRF guarding**: The `ai_http_request` command includes server-side request forgery protections to prevent the AI from accessing internal network resources.
- **Approval workflows**: Mutating tools such as `write_file` or `bash_background` return payloads that trigger UI approval cards; the backend executes these commands only after explicit user confirmation.

Security invariants are enforced within command implementations and verified via unit tests under `src-tauri/tests/`.

## Terminal Renderer Pool Architecture

To support multiple terminal tabs without unbounded memory growth, Terax AI implements a **bounded renderer pool** rather than creating separate Xterm.js and WebGL instances for every tab.

### Pool Management and Lifecycle

As implemented in [`src/modules/terminal/lib/rendererPool.ts`](https://github.com/crynta/terax-ai/blob/main/src/modules/terminal/lib/rendererPool.ts), the system maintains a maximum of `POOL_MAX_SIZE = 5` active renderer slots. Each slot owns a terminal instance, FitAddon, SearchAddon, SerializeAddon, and an optional WebGL addon. When a tab becomes visible, it acquires a slot via `acquire()`; when hidden, `release()` parks the slot without destroying it.

For tabs without an active renderer, the **DormantRing** buffer (defined in [`src/modules/terminal/lib/dormantRing.ts`](https://github.com/crynta/terax-ai/blob/main/src/modules/terminal/lib/dormantRing.ts)) preserves PTY output bytes without maintaining a live terminal instance. The architecture enforces a **never-serialize-mid-command invariant**: busy leaves are never snapshot-serialized, ensuring eviction occurs only for idle tabs.

## AI Subsystem Architecture

The AI engine is provider-agnostic and integrates with any OpenAI-compatible endpoint through the Vercel AI SDK v6.

### Agent Streaming and Tool Execution

The entry point `runAgentStream` in [`src/modules/ai/lib/agent.ts`](https://github.com/crynta/terax-ai/blob/main/src/modules/ai/lib/agent.ts) handles chat sessions using the SDK’s `streamText` function. Provider configurations mapping model IDs to endpoints, context limits, and cost metadata reside in [`src/modules/ai/config.ts`](https://github.com/crynta/terax-ai/blob/main/src/modules/ai/config.ts), supporting OpenAI, Anthropic, Google, Groq, Ollama, and LM Studio.

Tool definitions in [`src/modules/ai/tools/tools.ts`](https://github.com/crynta/terax-ai/blob/main/src/modules/ai/tools/tools.ts) include filesystem operations, search, shell commands, sub-agents, and managed agents. Tools marked with `needsApproval: true` trigger the approval workflow before execution. Session persistence uses `tauri-plugin-store` to save chat history to [`terax-ai-sessions.json`](https://github.com/crynta/terax-ai/blob/main/terax-ai-sessions.json) via [`src/modules/ai/lib/sessions.ts`](https://github.com/crynta/terax-ai/blob/main/src/modules/ai/lib/sessions.ts).

The React layer provides the AI composer interface through `AiComposerProvider` in [`src/modules/ai/lib/composer.tsx`](https://github.com/crynta/terax-ai/blob/main/src/modules/ai/lib/composer.tsx), managing input state, attachments, and voice handling while delegating actual tool execution to the Rust backend.

## Frontend Integration and React Glue

The frontend bootstrap begins at [`src/main.tsx`](https://github.com/crynta/terax-ai/blob/main/src/main.tsx), which creates the React root, registers global shortcuts, and mounts the main `App` component. Custom hooks such as `useZoom` ([`src/lib/useZoom.ts`](https://github.com/crynta/terax-ai/blob/main/src/lib/useZoom.ts)) provide webview zoom controls, while `usePresence` tracks window focus states.

The [`App.tsx`](https://github.com/crynta/terax-ai/blob/main/App.tsx) component establishes a **live context bridge** by calling `setLive({ getCwd, getTerminalContext, ... })`, enabling AI tools to query the current working directory and recent terminal output on demand without direct filesystem access.

## Code Examples: Interacting with the Terax AI Architecture

### Opening a PTY Session from React

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

async function startShell() {
  const ptyId = await invoke<string>('pty_open', {
    cwd: '/home/user',
    shell: 'bash',
  });
  console.log('PTY opened with id', ptyId);
}

```

The `pty_open` command is defined in [`src-tauri/src/modules/pty/mod.rs`](https://github.com/crynta/terax-ai/blob/main/src-tauri/src/modules/pty/mod.rs) and registers the pseudoterminal with the Rust backend.

### Reading Files Through the Security Layer

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

async function readFile(path: string) {
  const content = await invoke<string>('fs_read_file', { path });
  return content;
}

```

This invocation passes through the workspace authorization and deny-list validation before `fs_read_file` executes in the Rust module.

### Streaming AI Responses

```typescript
import { runAgentStream } from '@/modules/ai/lib/agent';

async function askAI(prompt: string) {
  const stream = runAgentStream({
    modelId: 'gpt-4o-mini',
    messages: [{ role: 'user', content: prompt }],
  });
  for await (const chunk of stream) {
    console.log(chunk);
  }
}

```

The `runAgentStream` function constructs the language model from provider configuration, streams responses via the Vercel AI SDK, and automatically injects the available tool set.

### Managing Terminal Renderer Slots

```typescript
import { acquireSlot, releaseSlot } from '@/modules/terminal/lib/rendererPool';

// When a tab becomes visible
const slot = acquireSlot(tabId);
// Attach slot.terminal to the DOM

// When the tab is hidden
releaseSlot(tabId);

```

The pool guarantees that at most five slots exist concurrently, parking idle slots to preserve memory.

## Summary

- **Two-process separation**: The Rust backend in `src-tauri/` owns all privileged operations, while the React frontend in `src/` remains sandboxed and communicates via Tauri IPC commands registered in [`src-tauri/src/lib.rs`](https://github.com/crynta/terax-ai/blob/main/src-tauri/src/lib.rs).
- **Strict security boundary**: Validation occurs in Rust through deny-lists, workspace authorization, SSRF guards in `ai_http_request`, and mandatory approval flows for mutating tools.
- **Bounded resource management**: The terminal renderer pool caps active Xterm.js instances at five, using `DormantRing` buffers to preserve output for inactive tabs without memory exhaustion.
- **Provider-agnostic AI**: The subsystem in `src/modules/ai/` uses Vercel AI SDK v6 to stream responses from multiple LLM providers, with tool execution subject to backend security checks.
- **Clean frontend integration**: React hooks and context providers bridge UI state to backend capabilities without violating the security perimeter.

## Frequently Asked Questions

### How does Terax AI prevent AI-generated commands from compromising the system?

Terax AI enforces a strict approval workflow where mutating tools—such as file writes or background shell commands—return payloads that the UI renders as approval cards. According to the crynta/terax-ai source code, the Rust backend executes these commands only after explicit user confirmation, and all AI network requests pass through SSRF guards in `ai_http_request` to prevent internal network access.

### Why does Terax AI limit terminal renderers to five instances?

The renderer pool in [`src/modules/terminal/lib/rendererPool.ts`](https://github.com/crynta/terax-ai/blob/main/src/modules/terminal/lib/rendererPool.ts) caps active instances at `POOL_MAX_SIZE = 5` to prevent memory exhaustion when users open many tabs. The architecture uses a **DormantRing** buffer to preserve PTY output for tabs without active renderers, ensuring smooth tab switching while maintaining bounded memory consumption.

### Can Terax AI connect to local LLM providers like Ollama or LM Studio?

Yes, the AI subsystem is provider-agnostic. The configuration file [`src/modules/ai/config.ts`](https://github.com/crynta/terax-ai/blob/main/src/modules/ai/config.ts) defines providers including OpenAI, Anthropic, Google, Groq, Ollama, and LM Studio. Users can stream responses from local endpoints using the same `runAgentStream` interface used for cloud providers.

### What happens when the React frontend needs to access the filesystem?

The frontend never accesses the filesystem directly. Instead, React components invoke commands like `fs_read_file` or `fs_write_file` through `window.__TAURI__.invoke`. These calls transit to the Rust backend, which validates the request against workspace authorizations and deny-lists before performing the operation, ensuring the sandboxed webview cannot bypass security controls.