Understanding the Structure of cmux v2 JSON Socket Protocol Requests and Responses

The cmux v2 JSON socket protocol uses a lightweight JSON-RPC 2.0 style API over Unix-domain sockets, requiring each request and response to be a single UTF-8 JSON line terminated by \n with specific envelope fields for routing and error handling.

The cmux v2 JSON socket protocol defines the wire format that external clients use to communicate with the terminal multiplexer daemon in the manaflow-ai/cmux repository. This protocol enables programmatic control of windows, workspaces, and terminal surfaces through a line-delimited JSON interface that follows JSON-RPC 2.0 conventions while using a simplified response envelope.

Request Envelope Structure

Every v2 protocol request must be a single line of UTF-8 JSON ending with a newline character. According to the source code in Sources/TerminalController.swift, the processV2Command method (lines 1885–2009) validates and dispatches incoming requests.

A valid request contains four required fields:

  • jsonrpc: Must be the string "2.0" to identify the protocol version
  • id: Any JSON value (typically a number or string) used to correlate responses
  • method: A dot-separated string identifying the operation (e.g., window.list, surface.send_text)
  • params: An optional JSON object containing method arguments; may be an empty object
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "window.list",
  "params": {}
}

The server validates that jsonrpc equals "2.0" and checks the method against an internal allow-list called focusIntentV2Methods before dispatching to the appropriate handler.

Response Format Specifications

Responses mirror the request's id field and use a boolean ok field to indicate success or failure. The v2Ok (lines 2923–2929) and v2Error (lines 2931–2939) helper functions in TerminalController.swift construct these envelopes.

Success Responses

A successful response returns ok: true with the result payload:

{
  "id": 1,
  "ok": true,
  "result": {
    "windows": [...]
  }
}

Error Responses

Failed operations return ok: false with a structured error object:

{
  "id": 1,
  "ok": false,
  "error": {
    "code": "method_not_found",
    "message": "Method does not exist",
    "data": null
  }
}

The error object always contains code (a short string identifier) and message (human-readable description), with an optional data field for additional context.

Encoding Rules

The v2Encode function (lines 2957–2966) handles serialization, ensuring that any internal newline characters within JSON values are escaped as \\n to maintain the single-line-per-message protocol invariant.

Method Taxonomy

The v2 protocol organizes commands into functional domains. The complete method dispatch table lives inside the switch method block within processV2Command.

System methods: system.ping, system.capabilities, system.identify, system.tree

Authentication: auth.login returns an object with authenticated and required boolean fields

Window management: window.list, window.current, window.focus, window.create, window.close

Workspace operations: workspace.list, workspace.create, workspace.select, workspace.current

Surface (pane) control: surface.list, surface.current, surface.focus, surface.send_text, surface.send_key, surface.refresh, surface.health

Debug utilities: debug.terminals

Protocol Implementation in Swift

The built-in ControlSocketClient class demonstrates the proper wire format. This example mirrors the implementation in TerminalControllerTests.sendV2Request:

import Foundation

let socketPath = "/tmp/cmux-debug.sock"
let client = ControlSocketClient(path: socketPath, responseTimeout: 2.0)

let request: [String: Any] = [
    "jsonrpc": "2.0",
    "id": 1,
    "method": "window.list",
    "params": [:]
]
let json = try JSONSerialization.data(withJSONObject: request)
let line = String(data: json, encoding: .utf8)! + "\n"

if let rawResponse = client.sendLine(line) {
    let data = rawResponse.data(using: .utf8)!
    let obj = try JSONSerialization.jsonObject(with: data) as! [String: Any]
    
    if let ok = obj["ok"] as? Bool, ok {
        print("Success →", obj["result"]!)
    } else {
        print("Error →", (obj["error"] as? [String: Any])?["message"] ?? "unknown")
    }
}

Python Client Example

For raw socket interaction without Swift dependencies:

import socket, json

socket_path = "/tmp/cmux-debug.sock"
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(socket_path)

payload = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "system.ping",
    "params": {}
}
line = json.dumps(payload) + "\n"
sock.sendall(line.encode("utf-8"))

response = b""
while not response.endswith(b"\n"):
    chunk = sock.recv(4096)
    if not chunk:
        break
    response += chunk

resp_obj = json.loads(response.decode())
print(resp_obj)  # → {'id': 1, 'ok': True, 'result': {'pong': True}}

sock.close()

Bash One-Liner with Socat

For quick debugging or shell scripts:

payload='{"jsonrpc":"2.0","id":1,"method":"window.list","params":{}}'
printf '%s\n' "$payload" | socat - UNIX-CONNECT:/tmp/cmux-debug.sock

Key Source Files

Understanding these files is essential for implementing compatible clients:

Summary

  • The cmux v2 JSON socket protocol requires single-line, newline-terminated JSON messages over Unix-domain sockets.
  • Requests must include jsonrpc: "2.0", an id, a method, and optional params.
  • Responses use an ok boolean field to indicate success, with result for data or error (containing code, message, and optional data) for failures.
  • Internal newlines are escaped by the v2Encode function to maintain the line-delimited format.
  • Method handlers are validated against an allow-list in processV2Command before execution.

Frequently Asked Questions

What transport layer does the cmux v2 protocol use?

The protocol communicates over Unix-domain sockets (AF_UNIX) using stream sockets (SOCK_STREAM). The default socket path is typically /tmp/cmux-debug.sock or a path specified during daemon startup, as defined in Sources/SocketControlSettings.swift.

How does the server handle malformed JSON or protocol violations?

The processV2Command function validates that the jsonrpc field equals exactly "2.0" and that the method exists in the focusIntentV2Methods allow-list. Invalid requests receive an error response with ok: false and an appropriate error code, while parse failures at the JSON level typically result in connection termination or silent dropping depending on the server's error handling context.

Can the id field be any JSON type, or must it be a number?

The id field can be any valid JSON value, including numbers, strings, or null, though the implementation typically uses integers. The server echoes this value verbatim in the response to allow clients to correlate asynchronous responses with their original requests.

Are batch requests (multiple JSON objects in one message) supported?

No, the v2 protocol does not support JSON-RPC batch requests. Each request must be a single JSON object terminated by a newline character (\n). The v2Encode function explicitly ensures responses are single-line by escaping internal newlines, and the parser reads line-by-line from the socket buffer.

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 →