# How the Runtime Host in Apache Maka Manages Lifecycles Across Desktop, TUI, and CLI

> Discover how Apache Maka's Runtime Host unifies lifecycles for Desktop, TUI, and CLI. Learn about shared bootstrapping and unified managers for consistent operation.

- Repository: [The Apache Software Foundation/maka](https://github.com/apache/maka)
- Tags: internals
- Published: 2026-09-02

---

**The Runtime Host in Apache Maka uses a single shared bootstrap ([`runtime-host-boot.ts`](https://github.com/apache/maka/blob/main/runtime-host-boot.ts)) and a unified `RuntimeHostDesktopManager` to handle lifecycles identically for both the Desktop TUI and CLI, with only UI-specific IPC channels and error dialogs differing between modes.**

Apache Maka's architecture deliberately collapses two seemingly different environments—an Electron-based Desktop interface and a headless command-line tool—into one codebase. According to the `apache/maka` source code, both entry points converge on the same **Runtime Host** core, ensuring that profile management, peer networking, and recovery logic behave identically whether a user clicks buttons or types commands.

## Bootstrapping the Runtime Host: One Entry Point, Two Clients

Every Runtime Host lifecycle begins in [`apps/desktop/src/main/runtime-host-boot.ts`](https://github.com/apache/maka/blob/main/apps/desktop/src/main/runtime-host-boot.ts). This module contains no Electron UI code, making it safe to import in both graphical and terminal contexts.

In the Desktop app ([`apps/desktop/src/main/main.ts`](https://github.com/apache/maka/blob/main/apps/desktop/src/main/main.ts)), the boot module loads after `app.whenReady()`:

```typescript
await import('./runtime-host-boot.js');   // ← Desktop boot

```

The CLI follows the identical pattern. Because [`runtime-host-boot.ts`](https://github.com/apache/maka/blob/main/runtime-host-boot.ts) is pure TypeScript/JavaScript, it executes in a bare Node.js process without `electron` initialized.

This design eliminates duplication: security patches, bug fixes, and new lifecycle phases apply to both interfaces simultaneously.

## Creating the RuntimeHostDesktopManager

The boot script instantiates a **`RuntimeHostDesktopManager`** through the `startRuntimeHostDesktopManager` helper. This manager becomes the single source of truth for host state across the entire process lifetime.

```typescript
runtimeHostManager = await startDesktopRuntimeHostWithRecovery({
  start: async () => {
    await localRuntimeHostRemoteAccess.recoverBeforeLocalHostStart();
    return startLocalRuntimeHostManager();
  },
  /* …recovery and error handling omitted… */
});

```

The `start` callback determines which host candidate to launch:
- **Production**: `@maka/runtime-host/execution-candidate-main`
- **Development/test**: A peer module when `--runtime-host-peer` is passed

> **Source location**: `startRuntimeHostDesktopManager` in [[`runtime-host-boot.ts`](https://github.com/apache/maka/blob/main/runtime-host-boot.ts)](https://github.com/apache/maka/blob/main/apps/desktop/src/main/runtime-host-boot.ts)

## Core Lifecycle Phases: Shared Implementation, Different Triggers

The `RuntimeHostDesktopManager` orchestrates six distinct phases. Each phase runs the same code regardless of interface, with only the invocation mechanism varying:

| Phase | Desktop TUI Trigger | CLI Trigger |
|-------|---------------------|-------------|
| **Startup** | `app.whenReady()` → boot import | Direct `node` execution of CLI entry |
| **Profile Enable** | Settings pane toggle or dialog | `maka host enable <profile>` subcommand |
| **Profile Disable** | Settings pane or context menu | `maka host disable <profile>` |
| **Default Profile Switch** | Settings → "Set as default" | `maka host default <profile>` |
| **Recovery** | Native diagnostic dialog ([`native-diagnostic-dialog.ts`](https://github.com/apache/maka/blob/main/native-diagnostic-dialog.ts)) | Console prompt (yes/no) |
| **Shutdown** | `app.quit()` → window-close hooks | Process exit → `runtimeHostManager.retireOwnedLocalHost()` |

### Startup: Storage Root and Catalog Initialization

Both interfaces resolve the same `userData` directory, load the **profile catalog** ([`runtime-host-client.json`](https://github.com/apache/maka/blob/main/runtime-host-client.json)), and initialize the **credential store**. The only divergence is timing: Electron's `app.whenReady()` provides an async signal that CLI processes lack.

### Profile Enable and Disable

The `runtimeHostProfileService.enable()` and `.disable()` methods—implemented in [`apps/desktop/src/main/runtime-host-profile-service.ts`](https://github.com/apache/maka/blob/main/apps/desktop/src/main/runtime-host-profile-service.ts)—handle the transition:

- Spin up the selected profile
- Establish peer connections (mesh or direct)
- Register IPC channels (Desktop) or JSON-RPC bindings (CLI)

```typescript
// Desktop UI example: enabling a remote host profile
import { runtimeHostProfileService } from './runtime-host-profile-service.js';

await runtimeHostProfileService.enable(
  {
    profile: { id: 'my-remote-host', kind: 'remote', /* … */ },
    credential: { token: '…' },
  },
  (peerEndpoint) => console.log('Peer endpoint:', peerEndpoint),
);

```

### Peer Mesh vs. Direct Peer

The manager consults configuration to decide connection strategy:

- **Mesh available**: Calls `openRuntimeHostPeerMeshOwner` (defined in [`runtime-host-peer-mesh-management.ts`](https://github.com/apache/maka/blob/main/runtime-host-peer-mesh-management.ts))
- **Mesh unavailable**: Falls back to `createRuntimeHostPeerClientFromEnvironment`

CLI scripts omit the status notifications that Desktop renders as toasts, but the underlying network code is identical.

### Recovery: Graceful Degradation with Interface-Appropriate Feedback

The `startDesktopRuntimeHostWithRecovery` wrapper catches startup failures—corrupted local host, missing updates, stale locks—and invokes `localRuntimeHostRemoteAccess.recoverBeforeLocalHostStart()`.

- **Desktop**: Spawns a modal via [`runtime-host-quit-copy.ts`](https://github.com/apache/maka/blob/main/runtime-host-quit-copy.ts) and [`native-diagnostic-dialog.ts`](https://github.com/apache/maka/blob/main/native-diagnostic-dialog.ts)
- **CLI**: Prints to `stderr` and awaits `readline` confirmation

> **Source**: Recovery logic in [[`runtime-host-boot.ts`](https://github.com/apache/maka/blob/main/runtime-host-boot.ts)](https://github.com/apache/maka/blob/main/apps/desktop/src/main/runtime-host-boot.ts)

### Shutdown: Identical Cleanup, Different Entry Points

When termination occurs, both interfaces execute:

```typescript
await runtimeHostManager.retireOwnedLocalHost();
// Tear down IPC channels
// Terminate host process

```

The Desktop adds window-close hooks; the CLI simply proceeds to `process.exit()` after cleanup completes.

## Transport and Messaging: Agnostic by Design

The **transport layer** lives in `packages/runtime-host/src/transport/` and knows nothing of Electron:

| Component | Location | Usage |
|-----------|----------|-------|
| `WebSocketTransport` | [`websocket-transport.ts`](https://github.com/apache/maka/blob/main/websocket-transport.ts) | Both Desktop renderer and CLI |
| `FramedTransport` | [`framed-transport.ts`](https://github.com/apache/maka/blob/main/framed-transport.ts) | Both Desktop renderer and CLI |

**Desktop UI**: Registers Electron IPC channels (`runtime-host-config-ipc-main`, `runtime-host-workspace-ipc-main`) that wrap the transport.

**CLI**: Calls the host's JSON-RPC API directly from Node, using the same `WebSocketTransport` class without IPC mediation.

> **Source**: [[`websocket-transport.ts`](https://github.com/apache/maka/blob/main/websocket-transport.ts)](https://github.com/apache/maka/blob/main/packages/runtime-host/src/transport/websocket-transport.ts)

## Key Abstractions Enabling Dual-Mode Operation

Four architectural decisions allow one codebase to serve two interfaces:

- **Profile Catalog**: Shared JSON file at [`userData/workspaces/runtime-host-client.json`](https://github.com/apache/maka/blob/main/userData/workspaces/runtime-host-client.json); UI renders it graphically, CLI parses it programmatically
- **Credential Store**: Same encrypted storage; UI may prompt interactively, CLI reads silently or accepts `--credential` flags
- **Remote Access API**: `localRuntimeHostRemoteAccess` exposes a local socket usable by any client
- **Package Resolution**: `runtimeHostSetupPackageResolver` (in [`runtime-host-setup-package.ts`](https://github.com/apache/maka/blob/main/runtime-host-setup-package.ts)) downloads host binaries; UI shows progress bars, CLI streams to stdout

All reside in the `@maka/runtime-host` package, ensuring that `apps/desktop/src/main/` contains only thin UI adaptation layers.

## CLI Lifecycle Example: Listing and Starting Profiles

```typescript
// CLI – starting a host and listing profiles
import { createClientRuntimeHostProfileCatalog } from '@maka/runtime-host/client';
import { resolveDesktopRuntimeHostStartup } from './runtime-host-boot.js';

(async () => {
  const userDataDir = '/home/me/.maka/desktop';
  const profileCatalog = await createClientRuntimeHostProfileCatalog(
    userDataDir,
    await createClientRuntimeHostCredentialStore(userDataDir),
  );

  const startup = await resolveDesktopRuntimeHostStartup(userDataDir, {
    catalog: profileCatalog,
    credentialStore: await createClientRuntimeHostCredentialStore(userDataDir),
  });

  console.log('Available profiles:');
  for (const p of startup.preferences.available) {
    console.log(`- ${p.id} (${p.kind})`);
  }
})();

```

This demonstrates that CLI code imports the same [`runtime-host-boot.js`](https://github.com/apache/maka/blob/main/runtime-host-boot.js) module and `createClientRuntimeHostProfileCatalog` factory used by the Desktop renderer process.

## Summary

- **Single bootstrap**: [`runtime-host-boot.ts`](https://github.com/apache/maka/blob/main/runtime-host-boot.ts) initializes the Runtime Host for both Desktop and CLI, leveraging pure Node.js compatibility
- **Unified manager**: `RuntimeHostDesktopManager` controls all lifecycle phases—startup, profile enable/disable, peer connection, recovery, and shutdown—without interface awareness
- **Transport agnosticism**: `WebSocketTransport` and `FramedTransport` serve IPC channels (Desktop) and direct JSON-RPC (CLI) equally
- **Shared state**: Profile catalog, credential store, and remote access API are identical across interfaces, differing only in presentation
- **Conditional UI**: Recovery dialogs, progress notifications, and interactive prompts adapt to the calling context while reusing core logic

## Frequently Asked Questions

### What happens if the Runtime Host fails to start in CLI mode?

The `startDesktopRuntimeHostWithRecovery` wrapper catches the failure, prints a diagnostic message to `stderr`, and prompts for yes/no confirmation via `readline`. No window is created; the process exits with a non-zero code if recovery is declined or fails. The same `recoverBeforeLocalHostStart()` logic runs regardless of interface.

### Can the CLI use peer mesh networking like the Desktop TUI?

Yes. The CLI can invoke `openRuntimeHostPeerMeshOwner` through the same `RuntimeHostDesktopManager` API. The Desktop UI adds visual status indicators for mesh topology, but the underlying mesh management in [`runtime-host-peer-mesh-management.ts`](https://github.com/apache/maka/blob/main/runtime-host-peer-mesh-management.ts) is fully available to CLI scripts.

### How does profile default switching work across both interfaces?

`runtimeHostManager.setDefaultProfile()` updates [`runtime-host-client.json`](https://github.com/apache/maka/blob/main/runtime-host-client.json) atomically. The Desktop Settings panel and the CLI command `maka host default <profile>` both call this method. Subsequent host startups respect the default regardless of which interface set it.

### Is the credential store accessible to both Desktop and CLI simultaneously?

Yes. The credential store is file-based with advisory locking. Both interfaces read from and write to the same encrypted storage at `userData/credentials/`. Race conditions are prevented by the store's internal synchronization, not by interface-specific coordination.