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

The Runtime Host in Apache Maka uses a single shared bootstrap (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. 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), the boot module loads after app.whenReady():

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

The CLI follows the identical pattern. Because 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.

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/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) 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), 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—handle the transition:

  • Spin up the selected profile
  • Establish peer connections (mesh or direct)
  • Register IPC channels (Desktop) or JSON-RPC bindings (CLI)
// 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:

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().

Source: Recovery logic in [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:

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 Both Desktop renderer and CLI
FramedTransport 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/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; 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) 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

// 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 module and createClientRuntimeHostProfileCatalog factory used by the Desktop renderer process.

Summary

  • Single bootstrap: 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 is fully available to CLI scripts.

How does profile default switching work across both interfaces?

runtimeHostManager.setDefaultProfile() updates 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.

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 →