# How to Use the cmux CLI to Create and Manage Workspaces Programmatically

> Programmatically create and manage your cmux workspaces using the CLI. Automate workspace operations from external scripts with this guide.

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

---

**The cmux CLI exposes a Unix-socket-based control API that allows external scripts and automation tools to create, rename, select, and close workspaces by sending simple string commands to `/tmp/cmux.sock`.**

The `cmux` terminal multiplexer from `manaflow-ai/cmux` provides a **Unix-socket-based control API** that enables programmatic workspace management without UI interaction. By connecting to the stable socket path and sending version-1 (v1) string commands, you can automate complex workspace workflows from Python, Bash, or any language capable of Unix socket communication.

## How the cmux Socket API Works

The programmatic interface follows a command-dispatcher architecture centered in [`Sources/TerminalController.swift`](https://github.com/manaflow-ai/cmux/blob/main/Sources/TerminalController.swift). When you send a command to the Unix socket, the **TerminalController** receives the raw string, splits it into a command key and JSON arguments, and executes it through a **socket-command policy** that tracks focus-mutation permissions.

According to the source code, the flow works as follows:

1. **Client connects** to the socket path defined by `SocketControlSettings.stableDefaultSocketPath` (typically `/tmp/cmux.sock`)
2. **TerminalController** parses the command via `withSocketCommandPolicy` (lines 2990‑3014) to determine if focus changes are permitted
3. **TabManager** (in [`Sources/TabManager.swift`](https://github.com/manaflow-ai/cmux/blob/main/Sources/TabManager.swift)) executes the actual workspace operations like `addWorkspace`, `selectWorkspace`, and `closeWorkspace`
4. **Response** returns as a newline‑terminated string starting with `OK` or `ERROR`

The command dispatcher maps string commands to private methods in `TerminalController`. For example, at line 1675, the `"new_workspace"` command maps to `newWorkspace(_:)` (lines 12178‑12190), which delegates to `TabManager.addWorkspace` (line 1193).

## Creating and Managing Workspaces with the Python Client

The repository includes an official Python client library at [`tests_v2/cmux.py`](https://github.com/manaflow-ai/cmux/blob/main/tests_v2/cmux.py) that wraps the socket protocol. This client translates method calls into v1 string commands and parses JSON responses automatically.

### Connecting to the Socket

Import the client and instantiate it to connect to the default socket path:

```python
from tests_v2.cmux import cmux

# Connects to /tmp/cmux.sock by default

c = cmux()

```

### Creating a New Workspace

Call `new_workspace()` to create a workspace and receive its UUID:

```python

# Returns the UUID of the newly created workspace

ws_id = c.new_workspace()
print("Created workspace:", ws_id)

```

As implemented in [`tests_v2/cmux.py`](https://github.com/manaflow-ai/cmux/blob/main/tests_v2/cmux.py) (lines 24‑30), this builds a `"workspace.create"` command string. The controller executes `newWorkspace(_:)` (lines 12178‑12190) and returns the workspace identifier.

### Listing and Selecting Workspaces

Retrieve current workspaces and switch focus programmatically:

```python

# List all workspaces with index, UUID, title, and selection status

for index, ws_uuid, title, selected in c.list_workspaces():
    print(f"{index}: {ws_uuid} ({title}) {'← selected' if selected else ''}")

# Select by UUID or index

c.select_workspace(ws_id)      # by UUID

c.select_workspace(0)          # by index (first workspace)

```

The `select_workspace` method sends `"workspace.select"` to the controller, which triggers `selectWorkspace(_:)` (line 1691) and calls `tabManager.selectWorkspace(_:)` (TabManager.swift line 2765).

### Renaming and Reordering Workspaces

Modify workspace metadata and positioning without UI interaction:

```python

# Rename current workspace or a specific one

c.rename_workspace("Production Server", ws_id)  # specific workspace

c.rename_workspace("Local Dev")                 # current workspace

# Reorder: move to specific index or before another workspace

c.reorder_workspace(ws_id, index=2)
c.reorder_workspace(ws_id, before_workspace=other_ws_id)

```

The rename command maps to `"workspace.rename"` and delegates to `TabManager.renameWorkspace(title:workspace:)`, while reordering triggers `TabManager.reorderWorkspace`.

### Closing Workspaces

Destroy workspaces and their associated surfaces:

```python
c.close_workspace(ws_id)   # destroys workspace and its surfaces

```

This sends `"workspace.close"` to `TerminalController.closeWorkspace(_:)`, which invokes `TabManager.closeWorkspace(workspaceId:)`.

### Complete Automation Example

Combine operations to set up an isolated environment:

```python
from tests_v2.cmux import cmux

c = cmux()

# 1️⃣ Create and name a workspace

ws = c.new_workspace()
c.rename_workspace("CI Build Environment", ws)

# 2️⃣ Focus it

c.select_workspace(ws)

# 3️⃣ Execute your automation here

# (Run commands, open splits, etc.)

# 4️⃣ Clean up

c.close_workspace(ws)
print("Workspace closed successfully")

```

## Raw Socket Communication Without a Library

For lightweight automation, you can communicate directly using standard Unix tools like `nc` (netcat) or `socat`. The protocol uses newline‑terminated ASCII commands with optional `key=value` arguments.

```bash
#!/usr/bin/env bash
SOCK="/tmp/cmux.sock"   # Use `cmux --print-socket-path` to verify

# Helper function to send commands

cmd() {
  printf "%s\n" "$1" | nc -U "$SOCK"
}

# Create workspace and capture UUID

WS_ID=$(cmd "workspace.create" | awk '{print $2}')
echo "Workspace ID: $WS_ID"

# Rename it

cmd "workspace.rename workspace_id=$WS_ID title='Bash Demo'"

# List workspaces

cmd "workspace.list"

```

Each command returns a single line starting with `OK` (followed by data) or `ERROR` (followed by a message). The Python client wraps this protocol to raise `cmuxError` exceptions on failures.

## Understanding Command Permissions and Policies

The cmux socket API implements a **focus-mutation policy** to prevent accidental UI disruptions. By default, commands in `focusIntentV1Commands` (such as `select_workspace`) can change focus, while others cannot unless explicitly permitted.

Key implementation details from the source:

- **Policy enforcement**: `withSocketCommandPolicy` (lines 2990‑3014) checks `socketCommandPolicyDepth` before allowing focus changes
- **Authentication**: The socket may require a password (`auth <pwd>`) unless the mode is set to `allowAll` in [`SocketControlSettings.swift`](https://github.com/manaflow-ai/cmux/blob/main/SocketControlSettings.swift)
- **Tagged instances**: When running debug builds with tags, each instance uses an isolated socket at `/tmp/cmux-debug-<tag>.sock`. Set the `CMUX_SOCKET` environment variable to target specific instances

## Summary

- **Unix socket location**: Default path is `/tmp/cmux.sock` (configurable via `SocketControlSettings`)
- **Command protocol**: Newline‑terminated v1 strings (`workspace.create`, `workspace.select`, etc.) with `OK`/`ERROR` responses
- **Python automation**: Use [`tests_v2/cmux.py`](https://github.com/manaflow-ai/cmux/blob/main/tests_v2/cmux.py) for type‑safe wrapper methods like `new_workspace()` and `select_workspace()`
- **Focus safety**: The controller enforces `socketCommandPolicyDepth` to prevent unauthorized focus changes from CLI commands
- **Direct access**: Any language capable of Unix sockets (Bash, Go, Rust) can send raw commands via `nc` or native socket libraries

## Frequently Asked Questions

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

By default, cmux creates its control socket at `/tmp/cmux.sock` as defined in `SocketControlSettings.stableDefaultSocketPath`. When running tagged debug builds, the path becomes `/tmp/cmux-debug-<tag>.sock`. Use `cmux --print-socket-path` to discover the active socket location.

### Can I change workspace focus via the CLI API?

Yes, but only through commands explicitly marked as focus‑intents. The `select_workspace` command (mapped to `"workspace.select"`) is permitted to change focus because it exists in `focusIntentV1Commands`. Other commands run under `socketCommandPolicyDepth` restrictions that prevent unexpected focus mutations unless explicitly configured.

### How does cmux authenticate socket connections?

Authentication depends on the socket mode configured in the Settings UI under *Automation → Socket Control*. The default mode may require a password sent as `auth <password>` before executing commands, or you can set the mode to `allowAll` to permit unrestricted local access. Check [`SocketControlSettings.swift`](https://github.com/manaflow-ai/cmux/blob/main/SocketControlSettings.swift) for implementation details on access modes.

### What programming languages can I use to script cmux?

Any language capable of opening Unix domain sockets can control cmux. The repository provides a reference Python client ([`tests_v2/cmux.py`](https://github.com/manaflow-ai/cmux/blob/main/tests_v2/cmux.py)), but you can implement the same protocol in Bash (using `nc` or `socat`), Go, Rust, Node.js, or Ruby. The protocol is simple ASCII: send a command string ending in newline, receive a response line starting with `OK` or `ERROR`.