How Nodeterm Separates Core Services from Shell Implementations

Nodeterm enforces a strict "core‑vs‑shell" architecture by defining an abstract CorePlatform interface in src/shared/platform.ts that core services consume, while concrete implementations for Electron and headless Node.js environments inject platform‑specific APIs at runtime through separate shell modules.

Nodeterm’s codebase maintains a rigorous separation between reusable business logic and platform‑dependent code. This architectural pattern allows terminal management, workspace persistence, and agent handling to run identically across desktop and server deployments. By analyzing how nodeterm separates core services from shell implementations, developers can adopt a testable, portable pattern for cross‑platform TypeScript applications.

The Three-Layer Architecture

Nodeterm organizes its codebase into three distinct layers, each with a specific responsibility and directory structure.

Core services reside in src/core/ and contain pure TypeScript modules implementing business logic. These files—such as src/core/pty-devices.ts, src/core/workspace-store.ts, and src/core/agent-status-mirror.ts—handle terminal emulation, tmux integration, and state persistence without importing Electron or browser APIs.

Desktop shell implementations live in src/main/ and provide the Electron main‑process integration. The concrete PlatformElectron class in src/main/platform-electron.ts wires core services to the OS, managing native menus, clipboard access, and window handling through Electron APIs.

Server edition shell provides a headless Node.js runtime in src/server/platform-server.ts. This implementation satisfies the same CorePlatform interface using standard Node.js HTTP/WebSocket servers and node:child_process, enabling browser‑based clients to connect without Electron dependencies.

The Platform Interface Contract

The separation hinges on a platform seam defined in src/shared/platform.ts. This file exports the abstract CorePlatform interface, which declares methods such as getEnv(), spawnPty(), and readFile().

Core modules import only this abstract type. They never reference concrete Electron or Node.js server implementations directly. Instead, they receive a platform instance through constructor injection, delegating all platform‑specific operations to this interface.

// src/core/pty-manager.ts (core service)
import { CorePlatform } from '@shared/platform';

export class PtyManager {
  constructor(private readonly platform: CorePlatform) {}

  async createTerminal(cmd: string) {
    // Delegates to shell‑specific implementation
    const pty = await this.platform.spawnPty({ command: cmd });
    return pty;
  }
}

Because src/core/pty-manager.ts depends only on the CorePlatform abstraction, it remains agnostic to whether it runs inside an Electron window or a headless Docker container.

Shell‑Specific Implementations

Concrete platform implementations reside in separate directories, allowing the same core code to execute in fundamentally different environments.

Electron Desktop Shell

The src/main/platform-electron.ts file implements CorePlatform using Electron‑specific APIs and native Node modules. It handles PTY creation through node-pty compiled for Electron’s ABI and manages system‑level features like native dialogs and clipboard access.

// src/main/platform-electron.ts
import { CorePlatform } from '@shared/platform';
import { execFile } from 'node:child_process';

export class PlatformElectron implements CorePlatform {
  async spawnPty(opts) {
    // Uses node-pty compiled for Electron's ABI
    const pty = await import('node-pty');
    return pty.spawn(opts.command, [], { cwd: opts.cwd });
  }

  // Additional methods for clipboard, dialogs, etc.
}

Headless Server Shell

The src/server/platform-server.ts file provides a lightweight alternative for server deployments. It implements the same interface using standard Node.js APIs, falling back to plain shell processes when tmux is unavailable and exposing functionality through WebSocket connections.

// src/server/platform-server.ts
import { CorePlatform } from '@shared/platform';
import { spawn } from 'node:child_process';

export class PlatformServer implements CorePlatform {
  async spawnPty(opts) {
    // Fallback to standard spawn in headless environments
    return spawn(opts.command, [], { cwd: opts.cwd });
  }

  // Minimal implementations for server contexts
}

Enforcement and Testing

Nodeterm actively prevents platform leakage through architectural tests. The file src/core/no-electron.test.ts contains assertions that verify core modules never import Electron or browser‑specific APIs.

This boundary enables unit testing in isolation. Because src/core/ modules depend only on the abstract CorePlatform interface, developers can inject mock implementations during testing without loading heavy Electron contexts or native binaries.

Summary

Frequently Asked Questions

What is the platform seam in nodeterm?

The platform seam is the abstract CorePlatform interface defined in src/shared/platform.ts. It acts as a boundary between platform‑agnostic core logic and platform‑specific shell implementations, allowing core modules to call methods like spawnPty() without knowing whether they run in Electron or a headless server.

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

Nodeterm enforces the boundary through a combination of architectural discipline and automated testing. Core modules reside in src/core/ and import only the abstract CorePlatform type, while src/core/no-electron.test.ts fails if any core file imports Electron APIs, ensuring the separation remains intact.

Can nodeterm run as a purely headless server?

Yes. By using the PlatformServer implementation in src/server/platform-server.ts, nodeterm can run the same core services found in src/core/agent-status-mirror.ts and src/core/workspace-store.ts without any GUI components, serving terminal sessions over WebSocket connections to browser‑based clients.

Where are the concrete platform implementations located in the nodeterm repository?

The desktop implementation lives in src/main/platform-electron.ts, which uses Electron APIs and native Node modules. The server implementation resides in src/server/platform-server.ts, which uses standard Node.js libraries. Both files implement the CorePlatform interface exported from src/shared/platform.ts.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →