# How the OmniRoute Electron Desktop App Is Structured and Built

> Discover the OmniRoute Electron desktop app architecture. Learn how it uses a main-process/preload-script/renderer setup with Next.js and electron-builder for efficient development.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: architecture
- Published: 2026-07-12

---

**OmniRoute uses a main-process/preload-script/renderer architecture where the Electron main process launches a Next.js server as a child process, exposes secure IPC channels via a preload script, and packages everything using electron-builder.**

The [diegosouzapw/OmniRoute](https://github.com/diegosouzapw/OmniRoute) repository ships a full-featured Electron desktop client that wraps a Next.js application. The architecture follows Electron’s classic pattern while adding sophisticated process management to run the web server locally, handle native OS integration, and provide secure communication between the UI and the operating system.

## Architecture Overview

The application is divided into three distinct layers that communicate through well-defined boundaries.

- **Main Process**: Controls the application lifecycle, creates the `BrowserWindow`, spawns the Next.js server as a child process, and manages system-level features like the tray and auto-updater. Located in [`electron/main.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/main.js).
- **Preload Script**: Runs in an isolated context with `contextIsolation: true` and bridges safe IPC calls between the renderer and main process. Located in [`electron/preload.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/preload.js).
- **Renderer Process**: The Next.js application running inside the `BrowserWindow`. It accesses native features exclusively through the typed `window.electronAPI` exposed by the preload script.

## Main Process Lifecycle

The entry point at [`electron/main.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/main.js) orchestrates the entire application. It resolves the correct Node.js executable compatible with the bundled Electron runtime using `resolveNodeExecutable`, then spawns the Next.js server using `spawn(nodeExecutable, [serverScript], {...})` with custom `NODE_PATH` and memory-optimized `NODE_OPTIONS`.

Key responsibilities include:

- **Window Management**: Creates the main `BrowserWindow` with Content Security Policy (CSP) headers and loads the UI via `mainWindow.loadURL(getServerUrl())`.
- **Server Bootstrap**: Uses [`electron/lib/resolveServerEntry.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/lib/resolveServerEntry.js) to choose between `server-ws.mjs` or [`server.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/server.js) as the server entry point.
- **Process Cleanup**: Implements graceful shutdown logic that stops the child server, waits for exit (with a 5-second timeout), and prevents file-lock issues that would break auto-updates.

## Secure IPC Bridge

Security is enforced through [`electron/preload.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/preload.js), which uses `contextBridge.exposeInMainWorld` to expose a typed `window.electronAPI` to the renderer. This API provides three main interaction patterns:

- **`invoke`**: For async two-way communication (e.g., checking for updates).
- **`send`**: For fire-and-forget messages.
- **`on`**: For event listeners that return a disposable function to safely remove listeners when components unmount.

TypeScript definitions in [`electron/types.d.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/types.d.ts) declare the `ElectronAPI` interface and augment the global `window` object, ensuring compile-time safety in the renderer code.

### Renderer Integration Example

```javascript
// In any React component (renderer) – show server status in the UI
useEffect(() => {
  const dispose = window.electronAPI.onServerStatus((data) => {
    setServerStatus(data.status);
    setPort(data.port);
  });
  return dispose; // automatically removes the listener when component unmounts
}, []);

```

```javascript
// Trigger an update check from a Settings page
async function checkUpdates() {
  const result = await window.electronAPI.checkForUpdates();
  if (!result.success) {
    alert('Update check failed: ' + result.error);
  }
}

```

## Build and Packaging Workflow

The build process transforms the Next.js application into a standalone Electron distributable through two coordinated steps.

### 1. Prepare Stand-alone Bundle

Running `npm run prepare:bundle` executes `scripts/build/prepare-electron-standalone.mjs`, which bundles the Next.js app into `../.build/electron-standalone`. This directory contains the server code, assets, and necessary dependencies.

### 2. Electron-Builder Configuration

The `npm run build` command triggers the preparation step and then runs `electron-builder`, reading configuration from the `build` section of [`electron/package.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/package.json).

Key configuration details include:

- **App Identity**: Defines `appId` and `productName` for the packaged application.
- **File Inclusion**: The `files` array specifies exactly what gets copied into the final app: [`main.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/main.js), [`preload.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/preload.js), [`loginManager.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/loginManager.js), [`processTree.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/processTree.js), and the bundled server directory.
- **Platform Targets**: Configures NSIS installers for Windows, DMG for macOS, and AppImage/DEB for Linux.
- **Extra Resources**: Bundles the Next.js server, native assets, and required `node_modules` into the application package.

## Runtime Server Management

When the packaged app starts, the main process handles Node.js execution carefully to ensure compatibility across platforms.

- **Executable Resolution**: `resolveNodeExecutable` locates a Node binary compatible with the bundled Electron runtime.
- **Environment Setup**: Spawns the server with custom environment variables including `NODE_PATH` and optimized `NODE_OPTIONS` for memory management.
- **Process Tree Safety**: [`electron/processTree.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/processTree.js) provides cross-platform utilities to kill the entire process tree safely, preventing zombie processes during restarts or shutdowns.

## Native OS Integration

The main process implements several native features through the `setupIpcHandlers` function and auxiliary modules.

### System Tray

The `createTray` function initializes a native tray icon with a context menu, allowing users to control the application without keeping the main window open.

### Auto-Updates

Using `electron-updater`, the main process configures `autoUpdater` with status messages sent to the renderer via IPC (`update-status`). The preload script exposes `checkForUpdates`, `downloadUpdate`, and `installUpdate` methods, allowing the UI to control the update flow.

### Autostart Configuration

- **macOS/Windows**: Uses `app.setLoginItemSettings` for native autostart management.
- **Linux**: Writes a `.desktop` file to `~/.config/autostart` via `enableLinuxDesktopAutostart`.

```javascript
// Enable autostart on Linux from a Preferences dialog
async function enableAutostart() {
  const ok = await window.electronAPI.enableAutostart();
  if (ok) console.log('Autostart enabled');
}

```

### Credential Management

The [`electron/loginManager.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/loginManager.js) module handles OAuth-style web-cookie login flows and persists credentials securely. The [`electron/sqlite-inspection.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/sqlite-inspection.js) module detects existing encrypted credentials before auto-generating secrets, preventing database lock conflicts.

## Key Source Files Reference

| File | Role |
|------|------|
| [`electron/main.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/main.js) | Main process entry point controlling window, tray, server lifecycle, IPC, and auto-updates |
| [`electron/preload.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/preload.js) | Secure bridge exposing `window.electronAPI` to the renderer with context isolation |
| [`electron/types.d.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/types.d.ts) | TypeScript definitions for the preload API ensuring type safety |
| [`electron/package.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/package.json) | Build scripts, dependencies, and electron-builder configuration |
| [`electron/lib/resolveServerEntry.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/lib/resolveServerEntry.js) | Chooses the correct server entry (`server-ws.mjs` or [`server.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/server.js)) for the child process |
| [`electron/processTree.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/processTree.js) | Cross-platform helper to kill process trees safely |
| [`electron/sqlite-inspection.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/sqlite-inspection.js) | Detects existing encrypted credentials before auto-generating secrets |
| [`electron/loginManager.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/loginManager.js) | Handles OAuth login flow and credential persistence |

## Summary

- **OmniRoute** combines a Next.js renderer with an Electron main process that manages a local server as a child process.
- **Security** is enforced through `contextIsolation` and a preload script that exposes a typed `window.electronAPI` for IPC communication.
- **Build workflow** uses `prepare:bundle` to create a standalone Next.js build, then `electron-builder` packages it with platform-specific installers (NSIS, DMG, AppImage).
- **Runtime management** includes Node executable resolution, graceful server shutdown with 5-second timeouts, and process tree cleanup via [`electron/processTree.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/processTree.js).
- **Native features** cover system tray integration, auto-updates via `electron-updater`, and cross-platform autostart (using `setLoginItemSettings` on macOS/Windows and `.desktop` files on Linux).

## Frequently Asked Questions

### How does the Electron app communicate with the Next.js server?

The main process spawns the Next.js server as a child process using `spawn()` with a resolved Node.js executable. The renderer then communicates with this server over HTTP through the `BrowserWindow`, while native OS features are accessed via IPC channels exposed through the preload script at [`electron/preload.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/preload.js).

### What build tool packages the OmniRoute desktop application?

The project uses **electron-builder**, configured in the `build` section of [`electron/package.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/package.json). It bundles the prepared Next.js server from `../.build/electron-standalone` along with the main process scripts and extra resources into platform-specific installers.

### How does the app handle auto-updates across platforms?

The main process integrates `electron-updater` and exposes update actions through the preload script. Status updates are sent to the renderer via IPC (`update-status`), while the UI can trigger `checkForUpdates`, `downloadUpdate`, and `installUpdate` through the secure `window.electronAPI` bridge.

### Why is there a separate process tree cleanup module?

The [`electron/processTree.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/processTree.js) module ensures that when the application shuts down or restarts, the entire Node.js server process tree is terminated cleanly across all platforms. This prevents file locks on the SQLite database and other resources, which would otherwise block auto-updates or cause data corruption.