# Architecture and Purpose of the Preload Script in TUUI for Exposing Safe APIs

> Explore the TUUI preload script architecture guarding Electron sandboxed renderers. Discover how it securely exposes safe APIs via contextBridge, enforcing least privilege for robust application security.

- Repository: [AIQL/tuui](https://github.com/ai-ql/tuui)
- Tags: architecture
- Published: 2026-02-23

---

**The TUUI preload script acts as a secure bridge between Electron's sandboxed renderer and privileged main process, exposing only whitelisted IPC channels through `contextBridge.exposeInMainWorld` to enforce the principle of least privilege.**

The `ai-ql/tuui` repository implements a secure Electron architecture that isolates the renderer process from Node.js and system APIs. At the core of this security model is the preload script located in [`src/preload/index.ts`](https://github.com/ai-ql/tuui/blob/main/src/preload/index.ts), which carefully exposes safe APIs to the frontend without compromising process isolation. This approach prevents common Electron vulnerabilities while enabling rich functionality like LLM configuration management and popup handling.

## Architecture of the TUUI Preload Script

### Context Bridge and Process Isolation

In [`src/preload/index.ts`](https://github.com/ai-ql/tuui/blob/main/src/preload/index.ts), the script uses `contextBridge.exposeInMainWorld` to selectively inject APIs into the renderer's global `window` object. This Electron API ensures that the renderer cannot directly access Node.js or Electron internals, maintaining a strict security boundary between the web content and system resources.

### IPC Channel Whitelisting

The preload script implements strict IPC whitelisting through two arrays: `mainAvailChannels` and `rendererAvailChannels`. These arrays enumerate permitted channel names including `msgRequestAppInfo`, `msgOpenExternalLink`, and `renderListenStdioProgress` (lines 7-21 and 23-30).

Before forwarding any IPC call, the wrapper methods validate the channel name against these lists. The `send`, `on`, `once`, `off`, and `invoke` methods check authorization, rejecting any attempts to use undefined channels and eliminating the risk of malicious renderer code invoking unintended main-process handlers.

## Safe API Exposure Pattern

### Main API Object (mainApi)

The `mainApi` object provides generic IPC capabilities including `send`, `on`, `off`, and `invoke` methods. These wrap `ipcRenderer` calls but enforce the whitelist validation, ensuring renderer code can only communicate through approved channels.

```javascript
// Sending a request from the renderer to open an external link
window.mainApi.send('msgOpenExternalLink', 'https://example.com')

```

```javascript
// Listening for progress updates from the main process
window.mainApi.on('renderListenStdioProgress', (event, progress) => {
  console.log('Build progress:', progress)
})

```

### Domain-Specific API Objects

Beyond generic IPC, the preload exposes specialized objects for distinct functional domains via separate `exposeInMainWorld` calls (lines 37-50, 111, 129, 147, 239, 270):

- **llmApis**: Manages LLM configuration through channels like `list-llms`
- **popupApis**: Handles popup window operations with methods like `open()`
- **startupApis**: Controls application startup behavior
- **mcpServers**: Interfaces with MCP server configurations
- **dxtManifest**: Provides access to manifest data

Each object initializes by invoking a main-process channel (e.g., `ipcRenderer.invoke('list-llms')`) and exposes type-safe getters and setters.

```javascript
// Fetching the current LLM configuration
const currentLlm = window.llmApis.get()
console.log('Active LLM:', currentLlm)

```

```javascript
// Opening a popup defined in the configuration
window.popupApis.open('settings')

```

```javascript
// Accessing startup configuration
const startup = window.startupApis.get()
if (startup.autoLaunch) {
  // Perform auto-launch logic
}

```

## Security Implementation Details

### Validation and Development Logging

When `NODE_ENV` equals `development`, the preload script logs every IPC invocation to the console within the `send` and `invoke` methods, aiding debugging while maintaining security checks in production.

The documentation at [`docs/src/en/electron-how-to/preload-script.md`](https://github.com/ai-ql/tuui/blob/main/docs/src/en/electron-how-to/preload-script.md) provides architectural guidance on extending these APIs while maintaining security boundaries.

## Summary

- The preload script in [`src/preload/index.ts`](https://github.com/ai-ql/tuui/blob/main/src/preload/index.ts) enforces Electron's security model by sandboxing the renderer process.
- **IPC whitelisting** via `mainAvailChannels` and `rendererAvailChannels` arrays prevents unauthorized channel access.
- **contextBridge.exposeInMainWorld** safely exposes only necessary APIs including `mainApi`, `llmApis`, `popupApis`, `startupApis`, `mcpServers`, and `dxtManifest`.
- Domain-specific objects provide type-safe access to functionality without direct Node.js access.
- Development mode logging assists debugging while maintaining production security.

## Frequently Asked Questions

### What is the role of src/preload/index.ts in TUUI?

The [`src/preload/index.ts`](https://github.com/ai-ql/tuui/blob/main/src/preload/index.ts) file serves as the secure bridge between TUUI's renderer and main processes. It executes before the renderer loads, setting up the `contextBridge` to expose only whitelisted IPC channels and specific API objects to the global `window` object, preventing direct access to Node.js APIs while allowing controlled communication.

### How does TUUI prevent unauthorized IPC channel access?

TUUI implements strict **IPC whitelisting** using two arrays: `mainAvailChannels` for main-process bound messages and `rendererAvailChannels` for renderer-bound messages. Every IPC method (`send`, `invoke`, `on`, etc.) validates the channel name against these arrays before execution (lines 7-30), rejecting any undefined channels and preventing arbitrary main process invocation.

### What API objects are exposed to the renderer process?

The preload script exposes six primary objects via `contextBridge.exposeInMainWorld`: `mainApi` (generic IPC methods), `llmApis` (LLM configuration), `popupApis` (popup management), `startupApis` (startup settings), `mcpServers` (MCP server management), and `dxtManifest` (manifest data). Each object provides domain-specific methods that wrap validated IPC calls.

### How can developers debug IPC communications in TUUI?

When `NODE_ENV` is set to `development`, the preload script automatically logs all IPC invocations to the console within the `send` and `invoke` methods. Developers can monitor these logs to trace message flow between processes, while the accompanying documentation at [`docs/src/en/electron-how-to/preload-script.md`](https://github.com/ai-ql/tuui/blob/main/docs/src/en/electron-how-to/preload-script.md) provides architectural guidance for extending the API safely.