Apache Maka Runtime Host: Understanding the Public Client/Protocol Boundary

The packages/runtime-host module in Apache Maka exposes a stable, JSON-based API over WebSocket, separating external client communication into a Protocol layer (types and validation) and a Transport layer (WebSocket implementation).

The runtime-host package serves as the public edge of the Apache Maka runtime, enabling external clients—such as web front-ends, SDKs, and microservices—to communicate with the execution host without accessing internal implementation details. This boundary is explicitly defined by two architectural layers that enforce message schemas, size limits, and error handling while hiding the complexity of the underlying execution engine.

Architecture of the Public Boundary

The boundary is strictly partitioned into declarative types and concrete transport logic. This separation allows the host to evolve its internal session management and policy engines while maintaining backward compatibility for connected clients.

Protocol Definition Layer

The Protocol layer declares the wire format using TypeScript types, constants, and error classes. It defines the JSON schema that all messages must follow, enforces UTF-8 validity, and specifies size constraints.

Key exported symbols from src/protocol/index.ts include:

  • EncodedProtocolMessage – The TypeScript type representing a serialized protocol message
  • RUNTIME_HOST_MAX_MESSAGE_BYTES – The hard limit on incoming message payload size
  • RuntimeHostProtocolError – Thrown when frames contain invalid JSON, oversized payloads, or malformed structure
  • RuntimeHostTransportError – Raised for transport-level failures such as queue saturation

According to the Apache Maka source code, these definitions ensure that any compliant client can generate valid messages using only the protocol exports, without importing transport internals.

Transport Implementation Layer

The Transport layer implements the bidirectional WebSocket channel that reads, writes, and validates protocol messages. The primary implementation resides in src/transport/websocket-transport.ts and extends the abstract FramedTransport base class from src/transport/framed-transport.ts.

Core components include:

  • WebSocketTransport – The concrete class implementing the RuntimeHostMessageTransport interface defined in src/transport/message-transport.ts
  • Back-pressure management – Enforces MAX_PENDING_WRITE_BYTES to prevent memory exhaustion
  • Graceful shutdown – Provides closeAfterFlush() to drain pending writes before closing and abort() for immediate termination

How Messages Flow Across the Boundary

The client-to-host interaction follows a strict sequence that isolates protocol validation from socket management:

  1. Connection establishment – The client opens a WebSocket to the host endpoint (e.g., ws://<host>/runtime).

  2. Transport instantiation – The host creates a WebSocketTransport instance, wiring the socket’s message, error, and close events.

  3. Inbound decoding – All incoming frames pass through decodeMessage in the protocol layer. This function:

    • Validates UTF-8 encoding
    • Parses JSON into an EncodedProtocolMessage object
    • Throws RuntimeHostProtocolError for malformed or oversized payloads
  4. Outbound encoding – Responses are encoded as Uint8Array (or Buffer) and passed to WebSocketTransport.write. The transport checks the pending-write queue against MAX_PENDING_WRITE_BYTES, raising a RuntimeHostTransportError with code outbound_queue_full if the buffer is saturated.

  5. Lifecycle management – The transport supports closeAfterFlush() for graceful shutdowns that complete pending writes, or abort() to terminate the connection immediately and propagate transport-level failures.

Implementing a Client for the Public Boundary

Clients interact with the boundary using standard WebSocket clients and the protocol exports. The following examples demonstrate compliant interaction patterns.

Connecting and Sending Messages

Clients must respect RUNTIME_HOST_MAX_MESSAGE_BYTES when serializing payloads:

import WebSocket from 'ws';
import {
  RUNTIME_HOST_MAX_MESSAGE_BYTES,
  type EncodedProtocolMessage,
} from '@apache-maka/runtime-host/protocol';

const socket = new WebSocket('ws://localhost:8080/runtime');

function encode(msg: unknown): EncodedProtocolMessage {
  const json = JSON.stringify(msg);
  const size = new TextEncoder().encode(json).byteLength;
  
  if (size > RUNTIME_HOST_MAX_MESSAGE_BYTES) {
    throw new Error('Message exceeds host maximum size');
  }
  return Buffer.from(json);
}

socket.on('open', () => {
  const request = { operation: 'session.start', params: {} };
  socket.send(encode(request));
});

Receiving and Decoding Messages

Inbound messages should be validated against the protocol schema:

socket.on('message', (data) => {
  try {
    const text = data.toString('utf8');
    const message = JSON.parse(text); // Mirrors host decodeMessage
    console.log('Received:', message);
  } catch (e) {
    console.error('Protocol violation:', e);
  }
});

Server-Side Transport Handling

Host implementations use the WebSocketTransport class directly to manage client sessions:

import { WebSocketTransport } from '@apache-maka/runtime-host/transport';

// `ws` is a connected WebSocket instance from a client
const transport = new WebSocketTransport(ws);

// Read with a 5-second timeout
transport.read(5000)
  .then(msg => {
    console.log('Client message:', msg);
    return transport.write(encode({ echo: msg }));
  })
  .catch(err => {
    if (err instanceof RuntimeHostTransportError) {
      console.error('Transport failure:', err);
    }
  });

Key Source Files in the Runtime Host Package

The boundary is implemented across five critical files in the packages/runtime-host directory:

File Path Responsibility
src/protocol/index.ts Defines EncodedProtocolMessage, size constants, and protocol-level error constructors
src/protocol/errors.ts Implements RuntimeHostProtocolError and RuntimeHostTransportError classes
src/transport/message-transport.ts Declares the RuntimeHostMessageTransport interface abstracting read/write operations
src/transport/websocket-transport.ts Concrete WebSocket implementation enforcing protocol limits and connection lifecycle
src/transport/framed-transport.ts Base class providing low-level framing and transport error handling

Summary

  • The public client/protocol boundary in packages/runtime-host consists of a Protocol layer (types and validation) and a Transport layer (WebSocket implementation).
  • Protocol enforcement occurs in src/protocol/index.ts, which validates UTF-8, JSON structure, and payload sizes against RUNTIME_HOST_MAX_MESSAGE_BYTES.
  • WebSocketTransport in src/transport/websocket-transport.ts manages the physical connection, back-pressure via MAX_PENDING_WRITE_BYTES, and graceful shutdown through closeAfterFlush().
  • Clients communicate using standard WebSockets and JSON payloads, relying on EncodedProtocolMessage types for type safety without requiring internal host dependencies.

Frequently Asked Questions

What is the maximum message size for the Maka Runtime Host protocol?

The host enforces a hard limit defined by RUNTIME_HOST_MAX_MESSAGE_BYTES (exported from src/protocol/index.ts). Clients must ensure serialized JSON payloads do not exceed this threshold, or the host will throw a RuntimeHostProtocolError upon receipt.

How does the host handle malformed or invalid client messages?

All inbound frames pass through the decodeMessage function, which validates UTF-8 encoding and JSON syntax. If validation fails, the host raises a RuntimeHostProtocolError before the message reaches internal business logic, ensuring the execution engine remains isolated from malformed input.

Can I implement a custom transport other than WebSocket?

Yes. The RuntimeHostMessageTransport interface (defined in src/transport/message-transport.ts) abstracts the transport mechanism. While WebSocketTransport is the reference implementation, any class implementing read() and write() methods adhering to the interface can substitute for WebSocket in specialized deployment scenarios.

What happens when the outbound message queue is full?

If pending writes exceed MAX_PENDING_WRITE_BYTES, the transport raises a RuntimeHostTransportError with the specific code outbound_queue_full. This back-pressure mechanism prevents memory exhaustion and signals to the host that it should apply flow control or shed load.

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 →