# What Are the Different Electron Processes in nodeterm’s Architecture?

> Explore nodeterm's Electron architecture with its five distinct processes: Main, Core, Preload, Renderer, and Server. Understand their isolation and communication.

- Repository: [eneskirca/nodeterm](https://github.com/eneskirca/nodeterm)
- Tags: architecture
- Published: 2026-08-26

---

**nodeterm separates functionality across five distinct process contexts—the Main process, Core layer, Preload bridge, Renderer process, and Server process—each isolated in specific `src/` folders and communicating only through narrow, well-defined APIs such as `CorePlatform` and `contextBridge`.**

nodeterm is an open-source terminal emulator built on Electron that strictly enforces process boundaries to maximize security and portability. Unlike monolithic Electron applications, nodeterm’s multi-process design isolates platform-specific native code from the React UI and core business logic. This architecture allows the **Electron processes in nodeterm** to reuse the same backend services across the desktop app, a browser-based Server edition, and potential future mobile versions without rewriting any service logic.

## The Five Process Contexts in nodeterm

nodeterm’s codebase organizes each responsibility into a dedicated folder under `src/`, ensuring that code only imports dependencies appropriate to its runtime environment.

### Main Process (Electron Node Context)

The **Main process** lives in `src/main/` and owns allElectron-native capabilities. It creates the application window, registers IPC handlers, manages native dialogs, and implements the **CorePlatform** interface in [`src/main/platform-electron.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/main/platform-electron.ts). This file serves as the bridge between the Electron-free Core layer and the operating system.

The Main process never imports renderer code. Instead, it exposes a strictly limited API to the renderer via the preload script, ensuring that privileged Node.js APIs remain inaccessible to the UI layer.

### Core Layer (Platform-Agnostic Services)

Located in `src/core/`, the **Core layer** provides pure, platform-agnostic services including PTY handling, workspace management, Git integration, and remote-SSH support. All Core code communicates with the host environment solely through the `CorePlatform` interface defined in [`src/core/platform.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/platform.ts).

This layer contains zero Electron imports. Enforcement tests such as [`src/core/no-electron.test.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/no-electron.test.ts) guarantee that the Core remains decoupled from Electron, allowing it to run unchanged inside the Main process or the Server edition.

### Preload Process (Security Bridge)

The **Preload process** executes in an isolated context defined by [`src/preload/index.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/preload/index.ts). It uses `contextBridge.exposeInMainWorld` to create a typed `window.nodeTerminal` API (declared in [`src/preload/index.d.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/preload/index.d.ts)). This bridge is the only path through which the Renderer process can request native functionality.

By narrowing the exposed surface to specific channels like `core-platform-call`, the preload script prevents arbitrary IPC access and maintains the security boundary between the untrusted renderer and the privileged main process.

### Renderer Process (React UI)

The **Renderer process** resides in `src/renderer/` and contains the React-based user interface responsible for rendering the terminal canvas, kanban boards, and settings panels. It never communicates directly with the Main process.

All native interactions flow through the `window.nodeTerminal` object injected by the preload script. For example, reading a file requires calling `window.nodeTerminal.readFile()`, which forwards the request via IPC to the Main process’s `CorePlatform` implementation.

### Server Process (Browser Edition)

The **Server process** in `src/server/` demonstrates nodeterm’s portability. It runs a plain Node.js HTTP and WebSocket server that serves the built renderer to a browser. Instead of Electron IPC, it uses a WebSocket RPC bridge defined in [`src/shared/rpc.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/rpc.ts).

The Server implements the same `CorePlatform` interface in [`src/server/platform-server.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/server/platform-server.ts), allowing the Core layer to operate in a browser-only environment without any Electron dependencies, as verified by [`src/server/no-electron.test.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/server/no-electron.test.ts).

## How Process Isolation Is Enforced

nodeterm maintains strict architectural boundaries through interface segregation and automated testing. The **CorePlatform** abstraction in [`src/core/platform.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/platform.ts) defines the contract that any host must implement—whether Electron Main or Node Server.

Automated safeguards prevent accidental coupling:

- **[`src/core/no-electron.test.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/no-electron.test.ts)** fails if any Core module imports Electron.
- **[`src/server/no-electron.test.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/server/no-electron.test.ts)** ensures the Server edition remains Electron-free.

These tests guarantee that services remain portable and that the Renderer cannot bypass the preload bridge to access native APIs directly.

## Implementation Examples

### Implementing CorePlatform in the Main Process

The Main process implements platform-specific operations while exposing them to the Core layer:

```typescript
// src/main/platform-electron.ts
import { CorePlatform } from '../../src/core/platform';
import { ipcMain } from 'electron';

export const electronPlatform: CorePlatform = {
  readFileAtomic: async (path) => {
    // Uses Node's fs APIs
  },
};

ipcMain.handle('core-platform-call', async (event, method, ...args) => {
  return (electronPlatform as any)[method](...args);
});

```

### Exposing the Bridge in the Preload Script

The preload script narrows the API surface available to the renderer:

```typescript
// src/preload/index.ts
import { contextBridge, ipcRenderer } from 'electron';

contextBridge.exposeInMainWorld('nodeTerminal', {
  invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args),
  readFile: (path: string) => ipcRenderer.invoke('core-platform-call', 'readFileAtomic', path),
});

```

### Consuming the Bridge from the Renderer

The React UI interacts with native functionality only through the exposed window object:

```tsx
// src/renderer/someComponent.tsx
import React from 'react';

export const SomeComponent = () => {
  const loadFile = async () => {
    const content = await window.nodeTerminal.readFile('/path/to/file.txt');
    console.log(content);
  };

  return <button onClick={loadFile}>Load File</button>;
};

```

## Summary

- nodeterm isolates functionality across five architectural processes: Main, Core, Preload, Renderer, and Server.
- The Core layer (`src/core/`) remains Electron-free through the `CorePlatform` interface, enabling reuse in multiple environments.
- Process boundaries are enforced by automated tests like [`src/core/no-electron.test.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/no-electron.test.ts) that block unauthorized imports.
- All Renderer-to-Main communication flows through the Preload bridge (`src/preload/`) using `contextBridge.exposeInMainWorld`.
- The Server edition (`src/server/`) delivers a browser-compatible version using WebSocket RPC via [`src/shared/rpc.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/shared/rpc.ts) instead of Electron IPC.

## Frequently Asked Questions

### What are the three primary Electron processes in nodeterm?

nodeterm’s architecture centers on three runtime contexts: the **Main process** (Electron Node), the **Renderer process** (Chromium UI), and the **Server process** (Node-only for browsers). The Core layer operates inside both Main and Server contexts, while the Preload process serves as the secure bridge between Main and Renderer.

### How does nodeterm prevent Electron dependencies from leaking into core services?

The codebase enforces a strict **CorePlatform** interface ([`src/core/platform.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/platform.ts)) that abstracts all platform operations. Automated tests in [`src/core/no-electron.test.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/core/no-electron.test.ts) parse the dependency graph and fail the build if any Core module imports Electron, ensuring the layer remains pure and portable.

### Why does nodeterm use a preload script instead of exposing IPC directly to the renderer?

Using [`src/preload/index.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/preload/index.ts) with `contextBridge.exposeInMainWorld` creates a **principle of least privilege** barrier. It narrows the renderer’s access to a whitelist of channels (such as `core-platform-call`) rather than exposing the full `ipcRenderer` object, preventing untrusted web content from executing arbitrary main-process commands.

### Can nodeterm run without Electron installed?

Yes. The **Server edition** located in `src/server/` runs as a standalone Node.js application serving the UI over HTTP and WebSocket. It implements `ServerPlatform` ([`src/server/platform-server.ts`](https://github.com/eneskirca/nodeterm/blob/main/src/server/platform-server.ts)) instead of the Electron-based platform, allowing users to access nodeterm from any modern browser without native desktop dependencies.