# How Apache Maka Manages the PTY Stack and Shell Execution: A Deep Dive into the Runtime Architecture

> Explore Apache Maka's runtime architecture. Learn how its PTY stack manages shell execution with pluggable I/O processing via PtyStack, PtyProcessDriver, and PtyScreenCollector.

- Repository: [The Apache Software Foundation/maka](https://github.com/apache/maka)
- Tags: deep-dive
- Published: 2026-08-31

---

**Apache Maka implements a layered, composable PTY stack that abstracts OS-level pseudo-terminal handling through three core modules—`PtyStack`, `PtyProcessDriver`, and `PtyScreenCollector`—enabling dynamic shell execution with pluggable I/O processing.**

The **PTY stack and shell execution** system in Apache Maka provides a modular foundation for terminal-based workflows. By decoupling the low-level PTY driver from higher-level features like screen capture and output filtering, Maka's runtime layer allows developers to extend terminal behavior without modifying core shell logic. This article examines the implementation details found in the `apache/maka` repository, focusing on how the runtime constructs, manages, and tears down this architecture.

## Core Architecture: Three-Layer PTY Stack Design

Maka's PTY management centers on a **stack-based composition pattern** implemented across three TypeScript modules in `packages/runtime/src/`.

### PtyStack: The Orchestration Layer

The `PtyStack` class in [[`packages/runtime/src/pty-stack.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/pty-stack.ts)](https://github.com/apache/maka/blob/main/packages/runtime/src/pty-stack.ts) maintains an ordered array of `PtyLayer` objects that process data in sequence.

- **`push(layer)`** — Adds a new layer to the top of the stack at runtime
- **`pop()`** — Removes the topmost layer, disabling its behavior instantly
- **`write(data)`** — Forwards input to the topmost layer, which cascades down to the driver
- **`onData` events** — Propagate from the bottom driver upward through each decorator

This bottom-up construction lets decorators intercept and transform data without the driver knowing they exist.

### PtyProcessDriver: The OS Interface

The `PtyProcessDriver` in [[`packages/runtime/src/pty-process-driver.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/pty-process-driver.ts)](https://github.com/apache/maka/blob/main/packages/runtime/src/pty-process-driver.ts) is the **only module that directly interacts with the operating system PTY**.

It wraps the **node-pty** library to:

- Spawn the configured shell (`/bin/bash`, `cmd.exe`, `zsh`, etc.) with user-specified arguments
- Emit raw byte streams as `data` events
- Expose `write()`, `resize()`, and `kill()` methods for stack layers to invoke

The driver remains agnostic to what happens above it in the stack.

### PtyScreenCollector: A Decorator Example

The `PtyScreenCollector` in [[`packages/runtime/src/pty-screen-collector.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/pty-screen-collector.ts)](https://github.com/apache/maka/blob/main/packages/runtime/src/pty-screen-collector.ts) demonstrates how decorators extend functionality.

- Parses ANSI escape sequences from driver output
- Builds a virtual screen model with line-by-line aggregation
- Exports **`getScreen()`** for UI components to retrieve buffer snapshots

Because it's a layer, it can be **swapped out at runtime** without restarting the shell process.

## Execution Flow: From Stack Construction to Shell Teardown

The **PTY stack and shell execution** lifecycle follows five distinct phases:

1. **Stack Construction** — `new PtyStack()` creates the container; `PtyProcessDriver` is pushed first as the foundation, followed by optional decorators like `PtyScreenCollector`

2. **Shell Launch** — The driver spawns the user-configured shell with inherited environment variables and working directory

3. **I/O Routing** — Input flows down (`stack.write()` → decorator → driver → PTY stdin), while output bubbles up (driver `data` event → decorator processing → UI handlers)

4. **Dynamic Layer Management** — Features activate or deactivate via `push()` and `pop()` without shell interruption

5. **Termination** — `stack.dispose()` triggers `kill()` on the driver, stream closure, and layer cleanup

## Practical Implementation: Creating a Configured PTY Session

The following example from the Maka source demonstrates stack assembly and usage:

```typescript
import { PtyStack } from '@maka/runtime';
import { PtyProcessDriver } from '@maka/runtime';
import { PtyScreenCollector } from '@maka/runtime';

const stack = new PtyStack();

// Bottom driver – spawns the real shell
const driver = new PtyProcessDriver({
  shell: process.env.SHELL ?? '/bin/bash',
  args: [],
  cols: 80,
  rows: 24,
});
stack.push(driver);

// Optional decorator – captures the screen buffer
const screen = new PtyScreenCollector();
stack.push(screen);

// Write user input (e.g., from a web UI)
stack.write('ls -la\n');

// Listen for processed output (after all decorators)
stack.onData(data => {
  console.log('Terminal output:', data);
});

// Retrieve the current screen snapshot (useful for UI rendering)
const snapshot = screen.getScreen();
console.log('Current screen buffer:', snapshot);

// Clean up when the session ends
stack.dispose();

```

Key configuration parameters for `PtyProcessDriver`:
- **`shell`** — Path to the executable (defaults to `process.env.SHELL`)
- **`args`** — Array of arguments passed to the shell
- **`cols`** / **`rows`** — Initial terminal dimensions

## Layer Composition Patterns and Use Cases

The stack architecture enables several **runtime-modifiable behaviors**:

| Pattern | Implementation | Benefit |
|---------|---------------|---------|
| Screen recording | Push `PtyScreenCollector` | Capture full terminal state for replay or debugging |
| Output filtering | Custom decorator layer | Sanitize or transform sensitive data before UI display |
| Command validation | Middleware layer | Intercept and approve/reject user input |
| Multi-tenant isolation | Driver-per-stack with shared decorators | Resource-efficient shell pooling |

Because layers are **pure decorators**, they compose predictably: order matters, but dependencies between layers are explicit through the stack interface.

## Testing and Reliability

Maka's test suite validates this architecture in:

- [[`packages/runtime/src/__tests__/pty-process-driver.test.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/__tests__/pty-process-driver.test.ts)](https://github.com/apache/maka/blob/main/packages/runtime/src/__tests__/pty-process-driver.test.ts) — Driver lifecycle, signal handling, and stream correctness
- [[`packages/runtime/src/__tests__/pty-screen-collector.test.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/__tests__/pty-screen-collector.test.ts)](https://github.com/apache/maka/blob/main/packages/runtime/src/__tests__/pty-screen-collector.test.ts) — ANSI parsing accuracy and snapshot API behavior

The separation of concerns allows isolated unit testing: mock drivers can verify decorator logic without spawning real shells, while driver tests focus solely on node-pty integration.

## Summary

- **PtyStack** ([`pty-stack.ts`](https://github.com/apache/maka/blob/main/pty-stack.ts)) provides the compositional container for ordered layer execution
- **PtyProcessDriver** ([`pty-process-driver.ts`](https://github.com/apache/maka/blob/main/pty-process-driver.ts)) encapsulates all OS-level PTY and shell management
- **PtyScreenCollector** ([`pty-screen-collector.ts`](https://github.com/apache/maka/blob/main/pty-screen-collector.ts)) illustrates the decorator pattern for output processing
- **Dynamic layer manipulation** via `push()`/`pop()` enables runtime feature toggling without shell restart
- **Bidirectional I/O routing** cascades input down and propagates events up through the stack

## Frequently Asked Questions

### What shell does Maka use by default?

Maka reads the `SHELL` environment variable and falls back to `/bin/bash` when unspecified. This default is hardcoded in `PtyProcessDriver` configuration logic in [`packages/runtime/src/pty-process-driver.ts`](https://github.com/apache/maka/blob/main/packages/runtime/src/pty-process-driver.ts).

### Can I add or remove PTY layers while a shell is running?

Yes. The `PtyStack.push()` and `PtyStack.pop()` methods operate at runtime. Adding a `PtyScreenCollector` captures subsequent output; removing it immediately stops screen collection without terminating the underlying shell process.

### How does Maka handle ANSI escape sequences?

The `PtyScreenCollector` layer parses ANSI codes to maintain a virtual screen model. Applications reading raw output can access pre-parsed screen state through `getScreen()`, while `onData` handlers receive the original byte stream.

### Is node-pty the only PTY backend supported?

The current implementation in [`pty-process-driver.ts`](https://github.com/apache/maka/blob/main/pty-process-driver.ts) exclusively uses node-pty. The stack architecture theoretically allows alternative drivers implementing the `PtyLayer` interface, though no additional drivers are present in the Apache Maka repository.