# MakaCU Lifecycle and RPC Protocol for Computer-Use Sandboxing: A Complete Technical Guide

> Explore the MakaCU lifecycle and RPC protocol for computer-use sandboxing. Learn how MakaCuService manages sandboxed processes with a strict JSON-RPC 2.0 protocol.

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

---

**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`](https://github.com/apache/maka/blob/main/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`](https://github.com/apache/maka/blob/main/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:

1. **Image directory purge**: The service clears the image directory specified in the constructor options to prevent stale screenshots.
2. **`host.hello` dispatch**: The host sends a JSON-RPC request containing the protocol version, host process ID (`hostPid`), image directory path, and global pointer permissions (`allowGlobalPointer`).
3. **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`](https://github.com/apache/maka/blob/main/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 `id` to each JSON-RPC request
- Tracks in-flight requests in a `pending` map 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 `$/cancel` with the request `id`
- 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_timeout` error

This logic is implemented in `requestCancel()` at lines 717–723 of [`maka-cu-service.ts`](https://github.com/apache/maka/blob/main/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:

1. The service enters the **backing_off** state
2. Waits for an exponential `restartBackoffMs` delay
3. Retries initialization up to `maxRestartAttempts` (configured in `startWithBudget()`, 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:

1. **SIGTERM**: The host sends a termination signal to the child process
2. **Grace period wait**: The host waits for `limits.shutdownGraceMs` (or a fallback of 3 seconds)
3. **SIGKILL**: If the process hasn't exited, the host forcefully kills it
4. **Cleanup**: The image directory is removed from the filesystem
5. **Event emission**: A `disposed` host-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 `$/cancel` for all pending requests belonging to the specified session
- Emits a `session_cleared` host-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`](https://github.com/apache/maka/blob/main/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 `result` envelopes or `error` objects with codes from `MAKA_CU_RPC_ERROR` (e.g., `-32700` for parse errors, `-32000` for 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`, or `unknown` (from `MAKA_CU_DISPATCH_OUTCOMES`)
- **Paths**: `ax_action`, `ax_attribute`, `ax_select`, `cg_event_pid`, `skylight_pid`, `cg_event_global`, or `none` (from `MAKA_CU_DISPATCH_PATHS`)
- **Tiers**: `ax`, `semantic-background`, or `coordinate-background` (from `@maka/core/computer-use`)
- **Effects**: `confirmed`, `unconfirmed`, or `none` (from `COMPUTER_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

```typescript
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

```typescript
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

```typescript
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

```typescript
// 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 `MakaCuService` class in [`maka-cu-service.ts`](https://github.com/apache/maka/blob/main/maka-cu-service.ts) orchestrates 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`](https://github.com/apache/maka/blob/main/maka-cu-protocol.ts).
- **Resilience**: Automatic restart with exponential backoff (`restartBackoffMs`) handles crashes, while `CANCEL_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.