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-peeris passed
Source location:
startRuntimeHostDesktopManagerin [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:
- Mesh available: Calls
openRuntimeHostPeerMeshOwner(defined inruntime-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.tsandnative-diagnostic-dialog.ts - CLI: Prints to
stderrand awaitsreadlineconfirmation
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
--credentialflags - Remote Access API:
localRuntimeHostRemoteAccessexposes a local socket usable by any client - Package Resolution:
runtimeHostSetupPackageResolver(inruntime-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.tsinitializes the Runtime Host for both Desktop and CLI, leveraging pure Node.js compatibility - Unified manager:
RuntimeHostDesktopManagercontrols all lifecycle phases—startup, profile enable/disable, peer connection, recovery, and shutdown—without interface awareness - Transport agnosticism:
WebSocketTransportandFramedTransportserve 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →