MakaCU Lifecycle and RPC Protocol for Computer-Use Sandboxing: A Complete Technical Guide
The MakaCU lifecycle is managed by the host-side MakaCuService class, which spawns a sandboxed maka-cu child process and communicates via a strict JSON-RPC 2.0 protocol defined in packages/computer-use/src/maka-cu-protocol.ts.
The MakaCU (Computer-Use) subsystem in the Apache Maka project provides the sandboxed backend that allows AI agents to observe the desktop, synthesize accessibility (AX) and Core Graphics (CG) events, and capture screenshots. Understanding the precise lifecycle and RPC protocol is essential for building reliable computer-use automation that respects sandbox boundaries and handles failures gracefully.
MakaCU Lifecycle Phases
The lifecycle of a MakaCU sandbox instance follows a strict state machine implemented in packages/computer-use/src/maka-cu-service.ts. Each phase has defined entry points, error handling strategies, and cleanup guarantees.
Start and Handshake
The lifecycle begins when MakaCuService.start() spawns the maka-cu binary with the host sub-command. Before any operational commands are accepted, the host performs a mandatory handshake sequence:
- Image directory purge: The service clears the image directory specified in the constructor options to prevent stale screenshots.
host.hellodispatch: The host sends a JSON-RPC request containing the protocol version, host process ID (hostPid), image directory path, and global pointer permissions (allowGlobalPointer).- Capability negotiation: The executor replies with its version, supported capabilities, and operational limits (including
shutdownGraceMs).
This handshake ensures protocol version compatibility and establishes resource boundaries before transitioning to the ready state. The implementation resides in maka-cu-service.ts lines 73–98.
Running State and Request Tracking
Once the handshake completes, the service enters the ready state and accepts operational RPC calls. All requests flow through MakaCuService.request() (lines 667–724), which:
- Assigns a unique numeric
idto each JSON-RPC request - Tracks in-flight requests in a
pendingmap for cancellation support - Enforces timeout constraints based on the negotiated limits
Supported operational methods include host.dispatch for action execution and host.snapshot for screen capture. The host never sends raw binary data over the RPC channel; instead, images are written to the host-owned image directory and referenced by path in the response.
Cancel and Timeout Handling
Long-running operations can be aborted via the $/cancel JSON-RPC method. When cancellation is requested:
- The host sends
$/cancelwith the requestid - The executor has CANCEL_GRACE_MS (2 seconds) to acknowledge and terminate the operation
- If the executor fails to respond within the grace period, the host kills the child process and raises a
request_timeouterror
This logic is implemented in requestCancel() at lines 717–723 of maka-cu-service.ts. The cancellation mechanism prevents runaway automation tasks from consuming resources indefinitely.
Restart and Backoff Strategy
MakaCU is designed to recover from executor crashes and protocol violations. When the child process exits unexpectedly or violates the protocol contract:
- The service enters the backing_off state
- Waits for an exponential
restartBackoffMsdelay - Retries initialization up to
maxRestartAttempts(configured instartWithBudget(), lines 14–31)
Protocol violations—such as returning a disallowed path for the requested tier or using global pointer events without allowGlobalPointer—trigger immediate termination via reportProtocolViolation(), followed by the restart sequence.
Graceful Shutdown and Disposal
Clean shutdown is orchestrated by MakaCuService.dispose() (lines 837–886). The disposal sequence ensures resource cleanup:
- SIGTERM: The host sends a termination signal to the child process
- Grace period wait: The host waits for
limits.shutdownGraceMs(or a fallback of 3 seconds) - SIGKILL: If the process hasn't exited, the host forcefully kills it
- Cleanup: The image directory is removed from the filesystem
- Event emission: A
disposedhost-event signals completion
This multi-stage approach prevents data corruption in the image directory and ensures the executor has time to release platform resources (e.g., accessibility observers).
Session-Scoped Cleanup
For multi-session environments, clearSession(sessionId) (lines 804–822) provides targeted cleanup:
- Issues
$/cancelfor all pending requests belonging to the specified session - Emits a
session_clearedhost-event - Preserves requests from other sessions, allowing concurrent automation tasks to continue uninterrupted
JSON-RPC 2.0 Protocol Specification
The MakaCU protocol is a closed-set contract defined in packages/computer-use/src/maka-cu-protocol.ts. All communication occurs over line-delimited JSON-RPC 2.0 messages on the child's stdin and stdout.
Message Format and Transport
The transport layer uses newline-delimited JSON (NDJSON). Every request is a JSON-RPC 2.0 object with a monotonically increasing id. The protocol strictly prohibits binary data transfer; all screenshots are written to the filesystem and referenced by absolute path in the JSON payload.
Key message types include:
- Requests (
host.hello,host.dispatch,host.snapshot): Host-to-executor commands with method-specific payloads - Cancellation (
$/cancel): Host-to-executor notification to abort a specific request ID - Responses: Executor-to-host results wrapped in JSON-RPC
resultenvelopes orerrorobjects with codes fromMAKA_CU_RPC_ERROR(e.g.,-32700for parse errors,-32000for protocol version mismatch)
Core RPC Methods
| Method | Direction | Purpose |
|---|---|---|
host.hello |
Host → Executor | Initialize connection, exchange version/capabilities |
host.dispatch |
Host → Executor | Execute a computer-use action (click, key press, scroll) |
host.snapshot |
Host → Executor | Capture current AX tree and/or screenshot |
$/cancel |
Host → Executor | Request cancellation of an in-flight operation |
The host.dispatch payload includes a dispatch specification (dispatchSpec) containing the toolCallId, sessionId, action tier, execution path, desired effect, and verification parameters.
Dispatch Result Validation
All dispatch results must conform to closed sets defined in the protocol file. The MakaCuDispatchResult envelope contains:
- Outcomes:
ok,refused,failed, orunknown(fromMAKA_CU_DISPATCH_OUTCOMES) - Paths:
ax_action,ax_attribute,ax_select,cg_event_pid,skylight_pid,cg_event_global, ornone(fromMAKA_CU_DISPATCH_PATHS) - Tiers:
ax,semantic-background, orcoordinate-background(from@maka/core/computer-use) - Effects:
confirmed,unconfirmed, ornone(fromCOMPUTER_USE_EFFECTS)
The host validates every response against these sets. Violations trigger MakaCuProtocolViolation and immediate process termination to maintain sandbox integrity.
Implementation Examples
Starting the Service and Executing Handshake
import { MakaCuService } from '@maka/computer-use';
// Instantiate with binary path and image directory
const cu = new MakaCuService({
binaryPath: '/usr/local/bin/maka-cu',
imageDir: '/tmp/maka-cu-images',
hostVersion: '0.1.0',
});
// Spawn process, purge images, send host.hello, await ready state
const handshake = await cu.ensureStarted();
console.log('Executor capabilities:', handshake.capabilities);
The ensureStarted() method handles the complete initialization sequence, returning the handshake response containing the executor's PID and operational limits.
Dispatching a Sandboxed Click Action
const dispatchSpec = {
toolCallId: 'click-uuid-123',
sessionId: 'session-42',
tier: 'coordinate-background',
path: 'cg_event_pid',
effect: 'confirmed',
verification: { method: 'none', observedChange: false },
// Coordinate parameters specific to the target application
x: 400,
y: 300,
pid: 12345
};
const envelope = await cu.call('host.dispatch', dispatchSpec);
if (!envelope.ok) {
throw new Error(`Dispatch failed: ${envelope.error.message}`);
}
const result = envelope as MakaCuDispatchResult;
console.log(`Action completed with outcome: ${result.outcome}`);
This example demonstrates the coordinate-background tier with CG event PID path, targeting a specific process without requiring AX permissions.
Cancelling Long-Running Operations
import { AbortController } from 'node:abort-controller';
const controller = new AbortController();
// Initiate a scroll operation that might take several seconds
const scrollPromise = cu.call(
'host.dispatch',
{ /* long scroll spec */ },
controller.signal
);
// Cancel after 1 second
setTimeout(() => controller.abort(), 1000);
try {
await scrollPromise;
} catch (error) {
if (error.code === 'aborted') {
console.log('Request cancelled within CANCEL_GRACE_MS');
}
}
When the abort signal triggers, the service automatically sends $/cancel to the executor and manages the 2-second grace period before forceful termination.
Graceful Shutdown
// Application cleanup routine
async function shutdown() {
await cu.dispose(); // SIGTERM → wait → SIGKILL → cleanup
console.log('MakaCU service disposed, images removed');
}
The dispose() method handles the complete shutdown sequence including the fallback 3-second timeout if the executor doesn't respond to SIGTERM.
Summary
- Lifecycle Management: The
MakaCuServiceclass inmaka-cu-service.tsorchestrates spawn, handshake, request tracking, restart, and disposal through a well-defined state machine. - RPC Protocol: Strict JSON-RPC 2.0 over line-delimited stdin/stdout with closed-set validation for outcomes, paths, tiers, and effects defined in
maka-cu-protocol.ts. - Resilience: Automatic restart with exponential backoff (
restartBackoffMs) handles crashes, whileCANCEL_GRACE_MS(2s) enables cooperative cancellation. - Resource Cleanup: Graceful shutdown uses SIGTERM/SIGKILL with
shutdownGraceMs(3s fallback) and automatically purges the image directory. - Sandbox Boundaries: Protocol violations (invalid tier/path combinations or unauthorized global pointers) trigger immediate termination via
reportProtocolViolation().
Frequently Asked Questions
What happens if the maka-cu process crashes during an automation task?
If the child process exits unexpectedly, MakaCuService enters the backing_off state and attempts to restart the process with exponential backoff (restartBackoffMs). The service retries up to maxRestartAttempts as configured in startWithBudget(). In-flight requests receive lifecycle errors, and the Runtime Host can query the service status to determine when the sandbox is ready again.
How does MakaCU handle screenshot data without blocking the RPC channel?
The protocol avoids binary data transfer over JSON-RPC. When host.snapshot is called, the executor writes image files to the host-specified imageDir (established during host.hello) and returns only the file path in the JSON-RPC response. The host reads the image data directly from the filesystem, keeping the RPC channel lightweight and responsive.
What is the difference between the AX and coordinate-background tiers in host.dispatch?
The AX tier uses macOS Accessibility APIs (ax_action, ax_attribute paths) to interact with UI elements semantically, while the coordinate-background tier uses Core Graphics events (cg_event_pid, cg_event_global paths) to synthesize mouse/keyboard actions at specific screen coordinates. The tier selection must match the path type in the dispatch specification; mismatches trigger MakaCuProtocolViolation and immediate process termination.
Can multiple automation sessions share a single MakaCU instance?
Yes, but with limitations. A single MakaCuService instance handles requests from multiple sessionId values concurrently. When a specific session ends, clearSession(sessionId) cancels only that session's pending requests without affecting others. However, since the executor runs as a single process, a fatal error in one session (such as a crash) triggers a full service restart, interrupting all active sessions.
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 →