What Are the Different Electron Processes in nodeterm’s Architecture?
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. 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.
This layer contains zero Electron imports. Enforcement tests such as 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. It uses contextBridge.exposeInMainWorld to create a typed window.nodeTerminal API (declared in 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.
The Server implements the same CorePlatform interface in 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.
How Process Isolation Is Enforced
nodeterm maintains strict architectural boundaries through interface segregation and automated testing. The CorePlatform abstraction in 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.tsfails if any Core module imports Electron.src/server/no-electron.test.tsensures 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:
// 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:
// 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:
// 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 theCorePlatforminterface, enabling reuse in multiple environments. - Process boundaries are enforced by automated tests like
src/core/no-electron.test.tsthat block unauthorized imports. - All Renderer-to-Main communication flows through the Preload bridge (
src/preload/) usingcontextBridge.exposeInMainWorld. - The Server edition (
src/server/) delivers a browser-compatible version using WebSocket RPC viasrc/shared/rpc.tsinstead 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) that abstracts all platform operations. Automated tests in 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 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) instead of the Electron-based platform, allowing users to access nodeterm from any modern browser without native desktop dependencies.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →