# Best Practices for Using the cmux Socket API to Control Workspaces

> Master the cmux socket API to programmatically control terminal workspaces. Learn best practices for creating, querying, and manipulating workspaces while preventing UI disruption. Enhance your automation workflow.

- Repository: [manaflow-ai/cmux](https://github.com/manaflow-ai/cmux)
- Tags: best-practices
- Published: 2026-03-29

---

**The cmux socket API exposes a Unix-domain socket interface that allows external scripts and automation tools to create, query, and manipulate terminal workspaces programmatically while enforcing focus policies to prevent UI disruption.**

The `manaflow-ai/cmux` repository provides a terminal multiplexer with a robust socket-based control system. The `TerminalController` class services API requests over a Unix-domain socket, validating commands and forwarding workspace operations to the main-thread `TabManager`. This architecture enables CI pipelines and automation agents to manage workspaces without user interaction, provided they respect the focus-policy guards that prevent accidental focus stealing.

## Understanding the cmux Socket API Architecture

The socket API is implemented in [`Sources/TerminalController.swift`](https://github.com/manaflow-ai/cmux/blob/main/Sources/TerminalController.swift) and operates as a single-threaded, main-actor service that receives plain-text or JSON commands. External clients connect to a Unix-domain socket—defaulting to `/tmp/cmux.sock` or a tag-specific path—and send line-delimited commands that the controller parses and executes.

### Core Command Flow

When the application starts via `TerminalController.start`, it initializes a `SocketListenerHealth` monitoring system and begins an accept loop on the Unix socket. Each incoming line is processed in `handleCommand(_:)`, which matches the command key against a dispatch switch. Workspace-related commands follow this routing:

- `new_workspace` → `newWorkspace(_:)` (line 1675)
- `list_workspaces` → `listWorkspaces()` (line 1672)
- `select_workspace` → `selectWorkspace(_:)` (line 1691)
- `current_workspace` → `currentWorkspace()` (line 1694)
- `close_workspace` → `closeWorkspace(_:)` (line 1678)

### Thread Safety and Main-Thread Execution

Because UI state lives exclusively on the main thread, every workspace operation wraps its execution in `DispatchQueue.main.sync`. For example, `newWorkspace` implements this protection at lines 12185-12189, ensuring thread-safe mutations while keeping the socket listener responsive. This design guarantees that socket commands never create race conditions with the graphical interface.

### Focus-Policy Guards

The API enforces strict focus-management policies through `socketCommandAllowsInAppFocusMutations()` and a static whitelist (`focusIntentV1Commands` and `focusIntentV2Methods`) defined at lines 18-27. Workspace commands generally do not change UI focus unless explicitly permitted. The `select_workspace` command respects the socket-policy flag, meaning the UI will only switch workspaces if the socket operates in a focus-allowed context (e.g., `-socketControlMode allowAll`). This prevents background automation scripts from disrupting an active user session.

## Essential Workspace Commands

The v1 plain-text protocol uses simple line-based commands that return `OK <data>` or error messages. For structured automation, the v2 JSON API provides versioned methods with typed parameters.

| Command | Purpose | Response Format |
|---------|---------|-----------------|
| `new_workspace <title>` | Creates a workspace | `OK <uuid>` |
| `list_workspaces` | Returns all workspaces | `* 0: <uuid> <title>` per line |
| `select_workspace <uuid\|index>` | Activates a workspace | `OK` or error |
| `current_workspace` | Queries active workspace | `OK <uuid>` or error |
| `close_workspace <uuid>` | Removes a workspace | `OK` or protection error |

The `listWorkspaces` implementation (lines 12171-12175) prefixes the current workspace with `*` and outputs zero-based indices, while `selectWorkspace` (lines 13184-13186) validates indices against the current tab count before switching.

## Recommended Usage Patterns

### Creating and Selecting Workspaces

To create a workspace and immediately switch to it, chain the commands using the UUID returned from creation:

1. Send `new_workspace MyProject` to receive `OK <uuid>`
2. Send `select_workspace <uuid>` to activate it

This sequence respects the focus-policy; the UI will only change if the socket context allows focus mutations. For guaranteed focus changes, launch cmux with `-socketControlMode allowAll`.

### Listing and Querying Workspaces

Use `list_workspaces` for workspace discovery in automation scripts. The output format provides both UUIDs and indices:

```text
* 0: abc-123 MainProject
  1: def-456 Secondary

```

Parse the `*` prefix to identify the currently active workspace without issuing a separate `current_workspace` call.

### Closing Workspaces Safely

Before removing workspaces, check for protection flags using `close_workspace`. The command invokes `workspaceCloseProtectedMessage()` to verify the workspace can be closed. Always use UUIDs rather than indices when closing workspaces to avoid accidental deletion if the tab order changes between commands.

### Index-Based vs UUID-Based Selection

While `select_workspace` accepts both UUIDs and zero-based indices, **prefer UUIDs for deterministic identification**. Indices are brittle when workspaces are reordered, whereas UUIDs remain stable for the workspace lifetime. Use index-based selection (e.g., `select_workspace 2`) only when the UUID is unknown and the tab order is static.

## Implementation Examples

### Bash One-Liners with netcat

For quick shell automation, use `nc` to send commands to the Unix socket:

```bash

# Use the environment variable or default path

SOCK=${CMUX_SOCKET:-/tmp/cmux.sock}

# Create workspace and capture UUID

WS_ID=$(echo "new_workspace demo" | nc -U "$SOCK" | awk '{print $2}')
echo "Created workspace $WS_ID"

# Activate the workspace

echo "select_workspace $WS_ID" | nc -U "$SOCK"

```

The `nc -U` command writes the line, reads the newline-terminated response, and `awk` extracts the UUID from the `OK` prefix.

### Swift ControlSocketClient

For native integration, use the `ControlSocketClient` class from [`Sources/ControlSocketClient.swift`](https://github.com/manaflow-ai/cmux/blob/main/Sources/ControlSocketClient.swift):

```swift
let client = ControlSocketClient(path: "/tmp/cmux.sock", responseTimeout: 2.0)

// Create workspace
if let reply = client.sendLine("new_workspace automation") {
    let wsId = reply.split(separator: " ").last!
    // Select it
    _ = client.sendLine("select_workspace \(wsId)")
}

```

Reference the UI tests in [`cmuxUITests/SidebarHelpMenuUITests.swift`](https://github.com/manaflow-ai/cmux/blob/main/cmuxUITests/SidebarHelpMenuUITests.swift) (line 971) for additional client usage patterns.

### Python Scripts for CI Automation

For robust CI pipelines, implement explicit socket handling with timeouts:

```python
import socket, os, json, sys

sock_path = os.getenv("CMUX_SOCKET", "/tmp/cmux.sock")

def send(cmd):
    with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
        s.settimeout(2.0)
        s.connect(sock_path)
        s.sendall((cmd + "\n").encode())
        return s.recv(4096).decode().strip()

# Create and select workspace

response = send("new_workspace ci-run")
if response.startswith("OK"):
    ws = response.split()[1]
    print("Created:", ws)
    print(send(f"select_workspace {ws}"))
else:
    sys.exit(1)

```

This implementation validates the `OK` prefix and raises exceptions on failure, allowing CI systems to capture errors appropriately.

### JSON v2 API for Structured Automation

For new automation code, prefer the v2 JSON API implemented in `TerminalController.handleJSON(_:)` around line 1010. It provides structured responses and better error handling:

```bash
REQ='{"method":"workspace.select","params":{"workspace_id":"'"$WS_ID"'"}}'
echo "$REQ" | nc -U "$CMUX_SOCKET"

```

The v2 API returns objects like `{"result":"OK"}` or detailed error codes, offering future-proof compatibility as the API evolves. See [`docs/v2-api-migration.md`](https://github.com/manaflow-ai/cmux/blob/main/docs/v2-api-migration.md) for migration guidance from v1 plain-text commands.

## Configuration and Security Best Practices

### Socket Path Management

Always set the `CMUX_SOCKET` environment variable or pass `--socket <path>` to the cmux CLI to ensure your script communicates with the correct, possibly-tagged socket instance. The default path configuration resides in [`Sources/SocketControlSettings.swift`](https://github.com/manaflow-ai/cmux/blob/main/Sources/SocketControlSettings.swift). When connecting immediately after launch, implement a polling backoff:

```bash
while ! nc -zU "$SOCK" 2>/dev/null; do sleep 0.2; done

```

### Focus Policy and Socket Control Modes

Respect the focus-intent whitelist defined in [`docs/socket-focus-steal-audit.todo.md`](https://github.com/manaflow-ai/cmux/blob/main/docs/socket-focus-steal-audit.todo.md). By default, workspace commands do not change UI focus. Use `-socketControlMode allowAll` only when the automation explicitly requires focus changes, such as during dedicated automation runs without active users.

### Response Validation and Error Handling

Validate that responses start with `OK` (v1) or contain `"result":"OK"` (v2). Treat any other prefix as a failure. The socket may return errors for protected workspaces or invalid indices; check `workspaceCloseProtectedMessage()` logic in the source to understand protection scenarios.

## Summary

- **Use UUIDs for stability**: Prefer UUID-based selection over indices to avoid errors when workspace order changes.
- **Respect focus policies**: Only use `-socketControlMode allowAll` when UI focus changes are explicitly required; default modes prevent disruption.
- **Validate responses**: Check for `OK` prefixes or JSON result fields to confirm command success.
- **Prefer JSON v2**: Adopt the v2 JSON API for new automation to ensure typed parameters and future compatibility.
- **Handle socket readiness**: Poll for socket existence with backoff immediately after launching cmux.
- **Leverage built-in clients**: Use `ControlSocketClient` in Swift or manual socket handling in Python/Bash with proper timeouts.

## Frequently Asked Questions

### What socket path does cmux use by default?

By default, cmux creates its control socket at `/tmp/cmux.sock`. However, when using tagged instances or custom configurations, the path may vary. Always check the `CMUX_SOCKET` environment variable or the settings defined in [`Sources/SocketControlSettings.swift`](https://github.com/manaflow-ai/cmux/blob/main/Sources/SocketControlSettings.swift) to locate the active socket for your session.

### How do I prevent my automation script from stealing focus?

The cmux socket API implements focus-policy guards through `socketCommandAllowsInAppFocusMutations()` and a whitelist of focus-intent commands. By default, workspace operations like `new_workspace` do not change UI focus. Only `select_workspace` can change focus, and only when the socket operates in `allowAll` mode. Run cmux without focus-permissive flags to keep automation non-intrusive.

### Should I use UUIDs or indices when selecting workspaces?

**Prefer UUIDs** for all deterministic automation. While `select_workspace` accepts zero-based indices (validated at lines 13184-13186 in [`TerminalController.swift`](https://github.com/manaflow-ai/cmux/blob/main/TerminalController.swift)), indices change when workspaces are reordered. UUIDs returned by `new_workspace` and `list_workspaces` remain stable for the workspace lifetime, preventing race conditions in concurrent automation.

### What is the difference between v1 and v2 socket APIs?

The v1 API uses plain-text commands like `new_workspace` and returns simple `OK` or error strings. The v2 API, accessible via JSON requests to the same socket, offers structured methods like `workspace.select`, typed parameters, and JSON error objects. According to [`docs/v2-api-migration.md`](https://github.com/manaflow-ai/cmux/blob/main/docs/v2-api-migration.md), the v2 API provides better error handling, versioned methods, and backward compatibility guarantees, making it the recommended choice for new automation implementations.