A deep dive into cmux

By

Learn how cmux combines terminals, browsers, notifications, SSH, session restore, and automation into one native workspace for coding agents.

~~~

I spend a lot of time in terminals.

I run coding agents, development servers, test suites, deployment commands, and SSH sessions. One project can easily need five or six terminals.

Then I open a second project.

The problem is no longer how to run a command. The problem is knowing where everything is and what needs my attention.

cmux is a native macOS terminal built for this kind of work.

It combines a fast terminal, vertical workspaces, split panes, browser tabs, notifications, session restore, SSH, and a command-line interface that can control the application.

It is especially interesting if you run several coding agents at the same time.

An agent can work in one workspace while I am in another. When it finishes or needs an answer, cmux marks that workspace as unread. I can jump directly to it instead of checking every terminal.

The built-in browser is not just a web view beside the terminal. It has a full automation interface. An agent can open the local site, inspect the page, click a button, read console errors, and take a screenshot.

And cmux does not require a special kind of agent.

It works with normal terminal programs. I can use Codex, Claude Code, OpenCode, a shell script, or a test runner. cmux provides the workspace and the primitives. I decide how to combine them.

That is what makes it compelling.

cmux is not an agent orchestrator that happens to include a terminal. It is a terminal that has learned how modern development works.

In this tutorial I will explain the model, install it, build a practical setup, automate the browser, connect to remote servers, and show how I would use it across my own projects.

The problem cmux solves

Running one coding agent is simple.

I open a terminal, move into a repository, start the agent, and give it a task.

Running five agents is different.

I need to remember:

  • which terminal belongs to which repository
  • which agent is still working
  • which agent needs approval
  • where the development server is running
  • where I left the test output
  • which browser tab belongs to the local app
  • which remote terminal is connected to which server

Normal terminal tabs help, but the tab bar becomes unreadable quickly.

A terminal multiplexer such as tmux adds persistent sessions and splits. That solves another part of the problem, especially on remote machines.

But tmux is not a complete desktop workspace. It does not include a browser, native macOS notifications, a graphical sidebar, or a DOM automation API.

Agent dashboards take the opposite approach. They understand tasks and agents, but they often put the work inside a new interface. I might lose the exact terminal workflow, keyboard shortcuts, and tools I already use.

cmux sits between those approaches.

flowchart LR
  T["Traditional terminal"] --> C["cmux workspace"]
  M["Terminal multiplexer"] --> C
  A["Agent dashboard"] --> C
  C --> P["Real terminal processes"]
  C --> B["Built-in browser"]
  C --> N["Attention and notifications"]
  C --> X["CLI and socket automation"]

It keeps real terminal processes and familiar shell tools.

Then it adds the pieces needed to manage many of them as one system.

Here is the practical difference:

CapabilityNormal terminaltmuxAgent dashboardcmux
Real terminal programsYesYesDependsYes
Vertical project listRareNoUsuallyYes
Split panesApp-dependentYesUsuallyYes
Built-in browserNoNoSometimesYes
Native attention indicatorsBasicNoYesYes
Scriptable interfaceApp-dependentYesProduct-dependentCLI and socket API
Remote persistenceNoYesProduct-dependentManaged SSH and remote tmux
Uses my existing agent CLIYesYesNot alwaysYes

cmux does not make every other tool obsolete.

If I only need one shell, a normal terminal is simpler. If I need a process to survive for months on a Linux server, tmux is still a great tool. If I want a hosted task queue with isolated cloud environments, I need more than a terminal.

cmux is strongest when my Mac is the command center and I want terminals, agents, local browsers, and remote machines in one programmable interface.

What cmux is

cmux is a free and open source macOS application. The source is available in the manaflow-ai/cmux repository under the GPL-3.0-or-later license.

The application is written as a native Mac app. Its terminal is based on Ghostty’s terminal library, called libghostty.

This is an important detail.

cmux is not drawing a pretend terminal inside a web application. It is using a proper terminal engine and native macOS interface components.

The result feels like a terminal first.

At the same time, cmux owns enough of the interface to add:

  • a vertical workspace sidebar
  • panes and surface tabs
  • an embedded browser
  • a notification feed
  • file and Markdown viewers
  • workspace metadata such as Git branches and listening ports
  • custom commands and repeatable layouts
  • a CLI and Unix socket API
  • managed SSH workspaces
  • session restore

The project describes these pieces as primitives rather than a prescribed workflow.

I think that is the right design.

Nobody has settled on one perfect way to work with coding agents. A flexible terminal can support the workflow I use today and the one I invent next month.

Install cmux

cmux requires macOS 14 or newer. It supports both Apple Silicon and Intel Macs.

The project recommends downloading the DMG from cmux.com, opening it, and dragging cmux into the Applications folder. That version updates itself with Sparkle.

I can also install it with Homebrew:

brew tap manaflow-ai/cmux
brew install --cask cmux

To update it later:

brew upgrade --cask cmux

The first launch opens one window with one workspace and a terminal.

The cmux command is automatically available in terminals opened inside cmux.

If I also want to call it from another terminal application, the installation guide recommends adding a symlink:

sudo ln -sf "/Applications/cmux.app/Contents/Resources/bin/cmux" /usr/local/bin/cmux

I can verify that the app and CLI can talk to each other:

cmux ping

The result should be:

pong

Learn the cmux mental model

cmux uses a few terms that sound similar at first.

The hierarchy is:

flowchart TD
  W["Window"] --> WS1["Workspace: flaviocopes.com"]
  W --> WS2["Workspace: prototyped.dev"]
  WS1 --> P1["Pane: development"]
  WS1 --> P2["Pane: verification"]
  P1 --> S1["Surface: Codex terminal"]
  P1 --> S2["Surface: shell terminal"]
  P2 --> S3["Surface: local browser"]
  S1 --> T1["Panel: terminal content"]
  S3 --> B1["Panel: browser content"]

A window contains everything

A window is a normal macOS window.

Most of the time I would use one window. I can open another with Command-Shift-N when I want a completely separate visual context.

Each window has its own sidebar and workspaces.

A workspace is a sidebar entry

A workspace is the main organizational unit.

The interface sometimes calls it a tab because it behaves like a vertical tab. The CLI, socket API, and environment variables call it a workspace.

I would create one workspace per repository or focused task:

  • flaviocopes.com
  • aiworkshop.sh
  • prototyped.dev
  • server operations

A workspace can hold several split panes.

Create one with Command-N. Jump to the first nine workspaces with Command-1 through Command-9.

A pane is a split region

A pane is one region of a workspace.

Use Command-D to split to the right. Use Command-Shift-D to split downward.

I can move between panes with Option-Command and an arrow key.

A typical web project could have an agent on the left and the browser on the right. A more operational setup might have logs above and an interactive shell below.

A surface is a tab inside a pane

Each pane has its own tab bar.

A surface is one of those tabs. It contains either a terminal or a browser.

This gives me two levels of organization:

  • workspaces switch the complete project context
  • surface tabs switch one tool inside a pane

For example, my left pane can have a Codex surface and a normal shell surface. My right pane can have the local site and its Cloudflare dashboard in separate browser surfaces.

Open a new surface with Command-T.

A panel is the content

A panel is the terminal or browser content inside a surface.

I rarely need this term when using the interface. Automation generally targets surfaces.

The distinction becomes useful when reading the CLI documentation or inspecting the JSON API.

Build a useful workspace by hand

I would begin without any configuration.

Open cmux and move into a project:

cd ~/www/flaviocopes.com

Rename the workspace to flaviocopes.com.

Start a coding agent in the first terminal:

codex

Split the workspace to the right with Command-D.

In the new pane, start the development server:

npm run dev

Now open a browser split with Command-Shift-L and go to:

http://localhost:3000

The workspace now has three responsibilities:

flowchart LR
  A["Codex\nedit and explain"] --- D["Astro dev server\nrun the site"]
  D --- B["cmux browser\ninspect the result"]

This simple layout already removes a lot of context switching.

The agent, server output, and actual page belong to one workspace. When I switch to prototyped.dev, all three disappear together. When I return, the complete context is still there.

The sidebar also discovers useful project state. It can show the working directory, Git branch, and ports opened by processes in the workspace.

That port detection matters more than it sounds.

When several projects choose ports such as 3000, 4321, 5173, or 8787, I do not want to search through terminal output to remember which one belongs to the selected workspace.

The shortcuts worth learning first

cmux has many shortcuts, but I would start with a small set:

ShortcutAction
Command-NNew workspace
Command-1 to Command-9Select a workspace
Command-TNew surface in the pane
Command-DSplit right
Command-Shift-DSplit down
Option-Command-ArrowMove between panes
Command-Shift-LOpen a browser split
Command-LFocus the browser address bar
Command-Shift-POpen the command palette
Command-IOpen the notification panel
Command-Shift-UJump to the newest unread item
Command-Shift-,Reload configuration

Every cmux-owned shortcut can be changed in Settings or in cmux.json.

cmux also supports two-step chords. If my muscle memory comes from tmux, I can use a prefix such as Control-B:

{
  "shortcuts": {
    "bindings": {
      "newSurface": ["ctrl+b", "c"],
      "showNotifications": ["ctrl+b", "i"],
      "toggleSidebar": "cmd+b"
    }
  }
}

The complete list is in the keyboard shortcut reference.

Configure the terminal and the application

cmux has two configuration layers.

Terminal rendering comes from Ghostty configuration. cmux-specific behavior comes from cmux.json.

Ghostty configuration

cmux reads Ghostty configuration from these locations:

~/.config/ghostty/config
~/Library/Application Support/com.mitchellh.ghostty/config

I can create the first one with:

mkdir -p ~/.config/ghostty
touch ~/.config/ghostty/config

A small configuration could look like this:

font-family = SF Mono
font-size = 14
theme = One Dark
scrollback-limit = 50000000
split-divider-color = #3e4451

This controls terminal concerns such as the font, theme, cursor, and scrollback.

cmux configuration

The application configuration lives here:

~/.config/cmux/cmux.json

cmux writes a commented template on first launch. The file accepts comments and trailing commas, even though its extension is .json.

I would start with the schema reference so my editor can complete the available keys:

{
  "$schema": "https://raw.githubusercontent.com/manaflow-ai/cmux/main/web/data/cmux.schema.json",
  "schemaVersion": 1,
  "app": {
    "appearance": "dark",
    "confirmQuit": "always"
  },
  "terminal": {
    "autoResumeAgentSessions": true
  },
  "browser": {
    "openTerminalLinksInCmuxBrowser": true,
    "hostsToOpenInEmbeddedBrowser": ["localhost", "*.pages.dev"]
  }
}

After editing the file, reload it with:

cmux reload-config

Or press Command-Shift-,.

The configuration reference documents the complete schema.

Use project-specific configuration

Global configuration is good for my personal shortcuts and appearance.

Repeatable project workflows belong in the repository:

.cmux/cmux.json

Project configuration can define actions, commands, layouts, and notification hooks. It cannot override every global application preference.

This separation is useful.

My font is personal. The command that starts a project is shared knowledge.

A repository can describe its ideal workspace without taking over the rest of my cmux setup.

cmux asks me to trust project commands before it runs them. That is important because cloning a repository should not silently authorize arbitrary shell commands.

I would review .cmux/cmux.json with the same care I give a package script or CI workflow.

Save a complete workspace layout

The fastest way to create a layout is visual.

Build the workspace by hand, right-click the plus button, and choose Save Workspace as Layout.

cmux records the splits, directories, running agents, and browser tabs as an action in cmux.json.

I can also write a layout directly.

Here is a project layout for this Astro site:

{
  "$schema": "https://raw.githubusercontent.com/manaflow-ai/cmux/main/web/data/cmux.schema.json",
  "schemaVersion": 1,
  "actions": {
    "flaviocopes-site": {
      "type": "workspace",
      "title": "Open flaviocopes.com",
      "restart": "confirm",
      "workspace": {
        "name": "flaviocopes.com",
        "cwd": "~/www/flaviocopes.com",
        "layout": {
          "direction": "horizontal",
          "split": 0.58,
          "children": [
            {
              "pane": {
                "surfaces": [
                  {
                    "type": "terminal",
                    "name": "Codex",
                    "command": "codex",
                    "focus": true
                  },
                  {
                    "type": "terminal",
                    "name": "Shell"
                  }
                ]
              }
            },
            {
              "pane": {
                "surfaces": [
                  {
                    "type": "terminal",
                    "name": "Astro",
                    "command": "npm run dev"
                  },
                  {
                    "type": "browser",
                    "name": "Local site",
                    "url": "http://localhost:3000"
                  }
                ]
              }
            }
          ]
        }
      }
    }
  }
}

The layout is a tree.

Every split has two children. A child can be another split or a pane. Every pane contains one or more terminal or browser surfaces.

This recursive model can describe a simple side-by-side layout or a complex grid.

The restart value controls what happens when a workspace with the same name already exists:

  • new always creates another one
  • ignore switches to the existing workspace
  • recreate replaces it without asking
  • confirm asks before replacing it

I prefer confirm while developing a layout. It protects a running session from an accidental click.

Turn repeated shell commands into actions

Not every task needs a full workspace.

cmux can put normal commands in the command palette:

{
  "commands": [
    {
      "name": "Validate blog posts",
      "keywords": ["seo", "blog", "check"],
      "command": "npm run seo:validate-posts",
      "confirm": false
    },
    {
      "name": "Build production site",
      "keywords": ["astro", "production"],
      "command": "npm run build",
      "confirm": true
    }
  ]
}

Simple commands run in the current terminal’s working directory.

That is convenient, but it can also be surprising. If a command must run from the repository root, make that explicit:

{
  "name": "Run project tests",
  "command": "cd \"$(git rev-parse --show-toplevel)\" && npm test",
  "confirm": true
}

The confirmation is worth keeping for commands that publish, deploy, migrate data, or otherwise change external state.

The custom commands guide also supports reusable agent actions, custom shortcuts, tab-bar buttons, and plus-button menus.

Make agents visible with notifications

The attention system is one of cmux’s best features.

A terminal can send a notification. cmux records it in the notification panel, marks the workspace unread, and can show a native macOS alert.

I can send one from any process running inside cmux:

cmux notify \
  --title "Build complete" \
  --body "flaviocopes.com built successfully"

When the workspace is visible, cmux avoids bothering me with a redundant desktop alert. When I am elsewhere, the workspace gets an unread indicator.

Press Command-I to see the notification panel. Press Command-Shift-U to jump to the newest unread workspace.

This changes the multi-agent loop:

sequenceDiagram
  participant Me
  participant Agent as Coding agent
  participant C as cmux
  Me->>Agent: Give it a task
  Me->>C: Switch to another workspace
  Agent->>Agent: Edit, test, and review
  Agent->>C: Send completion or input notification
  C-->>Me: Mark workspace unread
  Me->>C: Jump to newest unread
  C-->>Me: Focus the exact workspace and pane

cmux understands the OSC 9, OSC 99, and OSC 777 terminal notification sequences too. Programs that already emit those sequences can integrate without calling the CLI.

For my own long commands, I could use a small shell function:

notify-after() {
  "$@"
  local exit_code=$?

  if [ $exit_code -eq 0 ]; then
    cmux notify --title "Command complete" --body "$1 succeeded"
  else
    cmux notify --title "Command failed" --body "$1 exited with $exit_code"
  fi

  return $exit_code
}

Then:

notify-after npm run build

cmux also provides agent hooks. The convenient starting point is:

cmux hooks setup

That command looks for supported agent CLIs and installs the integrations it can find. I can target one agent explicitly:

cmux hooks setup codex

The hooks do more than notify. For supported agents, they can save the native session identifier needed for session restore.

Add status, progress, and logs to the sidebar

Notifications tell me that something happened.

Sidebar metadata tells me what is happening now.

A script can attach a status pill to its workspace:

cmux set-status build "building" \
  --icon hammer \
  --color "#ff9500" \
  --priority 80

It can show progress:

cmux set-progress 0.4 --label "Generating course downloads"
cmux set-progress 0.8 --label "Building the site"
cmux set-progress 1.0 --label "Done"

And it can append short structured log entries:

cmux log --level progress --source build "Astro build started"
cmux log --level success --source build "Production build passed"

When the process finishes, clear temporary state:

cmux clear-status build
cmux clear-progress

This is useful for scripts that have several visible phases.

I would not stream thousands of build lines into the sidebar. The terminal already handles detailed output. The sidebar should answer a smaller question: what is each workspace doing?

Control cmux from the command line

The interface and the CLI control the same model.

List the workspaces:

cmux list-workspaces

Request JSON when a script needs the result:

cmux list-workspaces --json

Create a workspace:

cmux new-workspace

Create a split:

cmux new-split right

Inspect the currently focused context:

cmux identify --json

List the surfaces in the current workspace:

cmux list-panels --json

Send input to the focused terminal:

cmux send "npm run build"
cmux send-key enter

Or target a specific surface:

cmux send --surface surface:3 "npm run seo:links"
cmux send-key --surface surface:3 enter

Targeting is the important part.

A script that assumes the right pane is focused will eventually type into the wrong terminal. A reliable integration discovers identifiers, stores the intended surface, and targets it explicitly.

cmux automatically sets these variables inside each terminal:

CMUX_WORKSPACE_ID
CMUX_SURFACE_ID
CMUX_SOCKET_PATH

A process can use them to act on the workspace and surface that launched it.

The CLI reference documents commands for workspaces, panes, surfaces, input, notifications, sidebar state, browsers, and utilities.

Understand the Unix socket API

The CLI talks to cmux over a Unix socket.

The release build normally listens here:

/tmp/cmux.sock

The protocol uses one newline-terminated JSON request per call:

{"id":"workspace-list","method":"workspace.list","params":{}}

A response uses the same request identifier:

{
  "id": "workspace-list",
  "ok": true,
  "result": {
    "workspaces": []
  }
}

Most integrations should start with the CLI. It already handles argument parsing, output, and errors.

The socket is useful when I am building a deeper integration or need many operations without starting a CLI process each time.

There are three access modes:

ModeWho can connect
offNobody
cmuxOnlyProcesses launched inside cmux terminals
allowAllAny local process

The default restricts access to descendants of cmux terminal processes.

That is a sensible security boundary. The API can create workspaces and send text into terminals. On a shared Mac, I would keep it off or restricted to cmux processes.

Use the built-in browser manually

Open a browser split with Command-Shift-L.

The browser uses WebKit, so the development tools are the familiar Safari tools. Toggle them with Option-Command-I. Open the JavaScript console with Option-Command-C.

For local web development, the benefit is not merely that the browser is nearby.

The browser belongs to the workspace.

When I switch from flaviocopes.com to prototyped.dev, I switch the agent, server, logs, and browser together. Cookies and page history remain attached to the relevant context.

Terminal links can open in the embedded browser. I can also configure specific hosts, such as localhost, to stay inside cmux.

This creates a compact feedback loop:

flowchart LR
  E["Edit code"] --> R["Dev server reloads"]
  R --> V["View in cmux browser"]
  V --> I["Inspect DOM, console, and network"]
  I --> E

Automate the browser

cmux exposes the browser through the same CLI.

Open a page:

cmux browser open http://localhost:3000

Discover the browser surface:

cmux browser identify

Suppose it returns surface:2.

Wait for the page to finish loading:

cmux browser surface:2 wait \
  --load-state complete \
  --timeout-ms 15000

Read the title and heading:

cmux browser surface:2 get title
cmux browser surface:2 get text "h1"

Create a compact representation of the interactive page:

cmux browser surface:2 snapshot --interactive --compact

Capture a screenshot:

cmux browser surface:2 screenshot --out /tmp/homepage.png

The browser CLI can also click, type, fill, select, scroll, and wait for text or selectors:

cmux browser surface:2 fill "#email" --text "[email protected]"
cmux browser surface:2 click "button[type='submit']" --snapshot-after
cmux browser surface:2 wait --text "Check your email"

It can inspect the browser console and page errors:

cmux browser surface:2 console list
cmux browser surface:2 errors list

This is a powerful agent workflow.

The agent does not need to declare victory after reading the source code. It can inspect the running site and prove that the result exists in the browser.

For a failure, I would ask it to collect three artifacts:

cmux browser surface:2 console list
cmux browser surface:2 errors list
cmux browser surface:2 screenshot --out /tmp/cmux-failure.png

The browser automation reference also covers cookies, local storage, frames, downloads, dialogs, JavaScript evaluation, and browser-state snapshots.

A practical verification script

Here is a small script I could use to verify the homepage after a change:

#!/bin/zsh

set -e

browser_surface="$1"
verification_result="failed"

if [ -z "$browser_surface" ]; then
  echo "Usage: ./verify-homepage.sh surface:ID"
  exit 1
fi

cleanup() {
  cmux clear-status verify

  if [ "$verification_result" = "failed" ]; then
    cmux notify \
      --title "Homepage verification failed" \
      --body "Check the verification terminal for details"
  fi
}

trap cleanup EXIT

cmux set-status verify "checking homepage" --color "#0a84ff"

cmux browser "$browser_surface" navigate \
  http://localhost:3000 \
  --snapshot-after

cmux browser "$browser_surface" wait \
  --selector "main" \
  --timeout-ms 10000

cmux browser "$browser_surface" get text "h1"
cmux browser "$browser_surface" errors list
cmux browser "$browser_surface" screenshot \
  --out /tmp/flaviocopes-homepage.png

verification_result="passed"
cmux notify \
  --title "Homepage verified" \
  --body "Saved screenshot to /tmp/flaviocopes-homepage.png"

The exact checks should match the task.

For a blog post I might verify the article heading and Open Graph metadata. For a course signup change I would check the form, confirmation text, and browser console. For a product page I would check the price and purchase link.

The important pattern is the same:

  1. change the code
  2. run the real application
  3. inspect the real page
  4. collect useful evidence
  5. notify me when the check is done

How I would use cmux for my site

My main site is an Astro project with a development server, a production build, content validation, internal-link checks, and Cloudflare deployment concerns.

I would use one flaviocopes.com workspace with three panes.

The large left pane would hold Codex and a normal shell as two surfaces. The upper-right pane would run the Astro development server. The lower-right pane would hold the browser.

flowchart TB
  subgraph WS["Workspace: flaviocopes.com"]
    direction LR
    subgraph LEFT["Pane 1"]
      C["Codex surface"]
      SH["Shell surface"]
    end
    subgraph RIGHT["Pane 2 and 3"]
      DEV["npm run dev"]
      WEB["http://localhost:3000"]
    end
  end
  C --> DEV
  DEV --> WEB
  WEB --> C

For a post like this one, the loop would be:

  • write and revise the Markdown in the agent surface
  • run post and link validation in the shell surface
  • keep Astro running in the server pane
  • open the scheduled article in the browser after temporarily making it visible locally
  • inspect the heading, code blocks, Mermaid diagrams, and metadata
  • run the full production build before handoff

I would put the validation commands in the project command palette:

npm run seo:validate-posts
npm run seo:links
npm run build

The site has a large post archive, so build output can take a while. A completion notification lets me continue another task without checking the build pane every minute.

How I would use cmux for several products

I maintain several independent projects under ~/www.

The natural mapping is one workspace per repository, grouped by purpose:

Content
  flaviocopes.com
  aiworkshop.sh

Products
  prototyped.dev
  stackplan.dev
  sitebase.dev

Operations
  Cloudflare
  servers

cmux supports collapsible workspace groups, so the sidebar can reflect this hierarchy without becoming one enormous flat list.

I would not open every project every morning.

I would save layouts for the projects I revisit often, then create them from the plus-button menu only when needed.

For prototyped.dev, I might have:

  • one agent working on the storefront
  • one shell running tests
  • one browser on the local product page
  • one browser on the deployed preview

For AI Workshop, I might keep the implementation workspace separate from the public-site workspace. That separation makes it harder to run the right command in the wrong repository.

The sidebar becomes a map of active work, not a list of every repository I own.

How I would use cmux with multiple coding agents

I would begin with one agent per workspace.

That is the easiest setup to reason about. Each agent gets its project, terminal, browser, and notifications.

When a task benefits from a second agent, I would add another surface or split. For example:

  • agent 1 implements a narrowly defined change
  • agent 2 reviews the relevant files and tests
  • I resolve any conflict in the shell surface

cmux can also integrate with agent-native team features. Claude Code teammates and supported oh-my-opencode subagents can appear as native cmux splits instead of being hidden inside tmux panes.

The important constraint is still file ownership.

Two agents editing the same file at the same time can overwrite each other’s ideas even when the terminal layout looks beautiful. cmux makes parallel work visible. It does not remove shared-state conflicts.

I would split work by independent files or use one writing agent and one read-only reviewer.

For related background, see my deep dive into Herdr. Herdr tackles the same general problem from a different direction: it runs as a persistent terminal multiplexer and models agent lifecycle state. cmux is the stronger fit when I want a native Mac workspace and programmable browser.

Connect to remote servers

cmux has a managed SSH mode:

cmux ssh [email protected]

Give the workspace a useful name:

cmux ssh [email protected] --name "production server"

Run an initial command after connecting:

cmux ssh [email protected] --command "cd /var/www/app && git status"

cmux reads normal host aliases and settings from ~/.ssh/config.

The managed workspace adds several features on top of a plain SSH terminal:

  • automatic reconnect with exponential backoff
  • remote agent notifications routed into the local sidebar
  • file and image drag-and-drop through scp
  • remote browser routing
  • persistent remote sessions

The browser routing is particularly useful.

If a development server runs on port 3000 of the remote machine, I can open http://localhost:3000 in that remote workspace’s browser. cmux sends the browser traffic through the remote connection.

I do not need to create an SSH -L port forwarding rule by hand.

Each remote workspace also gets an isolated cookie store. A login on one server does not leak into the browser context for another server.

The SSH documentation explains the relay daemon and security model. On first connection, cmux uploads a versioned remote helper and verifies it against an embedded SHA-256 manifest.

Combine cmux with remote tmux

cmux SSH sessions can reconnect, but tmux is still useful when the process must be owned by the remote machine.

cmux includes an opt-in remote tmux feature. Enable it under Settings → Beta Features, then connect with:

cmux ssh-tmux [email protected]

cmux uses tmux control mode to project the remote session into native cmux workspaces, tabs, and panes.

The mapping is:

tmuxcmux
sessionworkspace
windowsurface tab
panenative split

The tmux server remains the owner of the remote processes. cmux becomes the local interface.

This is a good combination for a long-running server task:

flowchart LR
  MAC["cmux on my Mac"] --> SSH["SSH control connection"]
  SSH --> TMUX["tmux on remote server"]
  TMUX --> A["agent"]
  TMUX --> L["logs"]
  TMUX --> S["server process"]

If the Mac disconnects, tmux keeps the work alive. When cmux reconnects, it mirrors the remote state again.

Remote tmux is currently marked beta, so I would test it on non-critical work before making it part of an operational routine.

Understand session restore

When cmux quits, it saves the shape of the application:

  • windows
  • workspaces
  • pane layouts
  • working directories
  • terminal scrollback, on a best-effort basis
  • browser URLs and navigation history

On relaunch, it rebuilds that state.

This is not the same as freezing arbitrary Unix processes.

A normal shell, development server, Vim session, or unsupported terminal program does not resume at the exact instruction where it stopped. cmux reopens the terminal context.

Supported coding agents are different. Hooks can record their native session identifiers, and cmux can run the agent’s own resume command after rebuilding the layout.

Set up the hooks after the agent CLI is installed:

cmux hooks setup

I can disable automatic agent resume while keeping layout restore:

{
  "terminal": {
    "autoResumeAgentSessions": false
  }
}

To manually reopen the previous session, use Command-Shift-O or:

cmux restore-session

The session restore guide is careful about the boundary: cmux restores app-owned state and supported agent sessions. It does not claim to checkpoint every process.

For arbitrary remote process persistence, I would still use tmux or another service manager.

Diagnose a slow workspace

Several agents, browsers, and development servers can use a lot of memory and CPU.

Activity Monitor might only show cmux and helper processes. It does not necessarily tell me which project caused the load.

cmux includes a task manager:

cmux top

Or open the command palette and choose Task Manager.

It shows resource usage organized by window, workspace, pane, surface, agent process, and browser web view.

That lets me trace the load back to the owning workspace instead of killing the entire application.

I would use it when:

  • the fans suddenly start
  • a browser pane consumes unusual memory
  • an agent appears stuck
  • an old development server is still running
  • too many background agents are active

The right fix might be closing one browser surface or stopping one test watcher, not restarting everything.

Security boundaries to understand

cmux is powerful because processes can control the interface.

That also means I should understand the boundaries.

Project configuration can run commands

A repository’s .cmux/cmux.json can define shell commands and startup layouts.

cmux presents a trust prompt, but I still need to read the configuration before approving it.

Treat it like any executable project configuration.

The socket can type into terminals

The CLI and socket API can send text and key presses to a surface.

Keep the default cmuxOnly access mode unless external automation truly needs allowAll.

On a shared machine, consider turning the socket off.

Browser state can contain secrets

Browser cookies and saved state can contain authenticated sessions.

Do not commit files created by cmux browser ... state save. Store them in a temporary or ignored directory and protect them like credentials.

Avoid putting passwords directly in shell history. Pass sensitive values through an appropriate secret mechanism instead of writing them into a reusable layout.

Sending terminal input is not the same as running a command safely

This sequence is convenient:

cmux send --surface surface:3 "some command"
cmux send-key --surface surface:3 enter

But it assumes the target is a shell waiting for a command.

If that surface is inside Vim, an agent prompt, or a confirmation dialog, the text can have a completely different meaning.

Reliable automation should identify the target and understand its current state before sending input.

Common problems

cmux is not found outside the app

The CLI is automatically available inside cmux terminals.

For other terminal applications, create the symlink shown during installation:

sudo ln -sf "/Applications/cmux.app/Contents/Resources/bin/cmux" /usr/local/bin/cmux

The CLI cannot connect

First check whether cmux is running:

cmux ping

Then inspect the socket mode in Settings. By default, a process outside cmux’s terminal ancestry cannot control the app.

Use:

cmux capabilities --json

to inspect the methods and current access mode.

Agent notifications do not appear

Install or refresh the integration after installing the agent CLI:

cmux hooks setup

Then send a direct test:

cmux notify --title "Test" --body "cmux notifications work"

If the direct test works, the problem is in the agent hook rather than the notification system.

Remember that desktop banners are suppressed when the relevant workspace is already active. Check the notification panel and unread state.

The restored terminal did not keep its running process

That is expected for arbitrary local processes.

cmux restores the layout and can resume supported agent conversations. It does not checkpoint every program.

Use tmux, a service manager, or managed remote sessions when the process itself must survive independently.

A project layout does not load

Validate the configuration:

cmux config doctor

Reload it:

cmux reload-config

Check that the file is in either ~/.config/cmux/cmux.json or the repository’s .cmux/cmux.json, and keep schemaVersion at 1.

A browser check is flaky

Do not add arbitrary sleeps.

Wait for the actual condition:

cmux browser surface:2 wait --selector "main" --timeout-ms 10000
cmux browser surface:2 wait --text "Saved"
cmux browser surface:2 wait --url-contains "/dashboard"

Condition-based waits are faster when the page is fast and more reliable when it is slow.

cmux compared to Ghostty

cmux uses Ghostty’s terminal engine and reads Ghostty terminal configuration, but the products solve different problems.

Ghostty is a general terminal emulator.

cmux builds a multitasking workspace on top of the terminal engine. It adds the vertical sidebar, workspace hierarchy, notifications, browser, automation, session metadata, and agent integrations.

I would choose Ghostty when I want a focused terminal emulator.

I would choose cmux when organizing several projects and agents is the larger problem.

cmux compared to tmux

tmux runs inside a terminal and works on macOS, Linux, and remote servers. It is mature, portable, and excellent at keeping processes alive after disconnects.

cmux is a native macOS application. It has a graphical sidebar, native shortcuts and notifications, a built-in browser, and deep local automation.

The choice is not always either-or.

I can use tmux on a remote server and let cmux present that session through its native interface. I can also run tmux in a normal cmux terminal.

Use tmux when the remote machine must own the session.

Use cmux when the Mac should own the complete working interface.

cmux compared to Herdr

cmux and Herdr both make several terminal agents manageable, and both expose programmatic control.

Their centers of gravity are different.

cmux is a native Mac application built around workspaces, attention, browser verification, and desktop integration.

Herdr is a terminal multiplexer built around persistent PTYs and semantic agent states such as working, blocked, done, and idle. It can run directly on macOS or Linux and looks the same over SSH.

I would choose cmux for a polished local development cockpit.

I would choose Herdr when terminal-native persistence and remote portability matter more than a built-in browser.

I can also run Herdr inside cmux. cmux owns the Mac window and browser. Herdr owns persistent terminal processes on the local or remote machine.

When cmux is a good fit

cmux is a strong fit when:

  • I use macOS
  • I work in several repositories at once
  • I run multiple coding agents
  • I want to know which agent needs attention
  • I build web applications and want the browser beside the terminal
  • I want agents to verify changes in a real page
  • I connect to development servers over SSH
  • I like creating my own workflows from small, scriptable tools

It is less compelling when:

  • I use Linux or Windows as my desktop
  • one terminal window already covers my work
  • every task runs in a hosted environment
  • I need arbitrary local processes to survive an application restart
  • I want a complete opinionated agent platform instead of building blocks

The macOS requirement is the largest boundary. cmux can control remote Linux work, but the application itself runs on a Mac.

I would introduce cmux in stages.

First, use it as a normal terminal.

Create one workspace per active project. Learn workspace switching, surface tabs, and splits.

Second, add the browser.

Keep the local application beside the development server. Use the browser console and screenshots during verification.

Third, set up agent hooks:

cmux hooks setup

Let notifications replace the habit of checking every terminal.

Fourth, save one workspace as a layout.

Choose the project you open most often. Do not build a universal configuration on the first day.

Fifth, add two or three command-palette actions for repetitive, safe operations.

Finally, automate browser checks and sidebar status only where they remove real friction.

This order matters because cmux is useful before it is customized. I can learn which parts of my workflow repeat, then encode those parts instead of guessing.

Final thoughts

The most interesting thing about cmux is not any single feature.

Vertical tabs exist elsewhere. Split panes exist elsewhere. Embedded browsers exist elsewhere. Terminal automation exists elsewhere.

cmux puts them behind one coherent model.

A workspace contains the terminals, agents, browser, notifications, and project state for one piece of work. The graphical interface and command-line interface control the same objects. A person can click a pane while an agent targets its surface identifier.

That makes cmux useful at two levels.

It is a better place for me to see the work.

And it is a programmable environment in which agents can do the work.

That combination is exactly what I want from a modern terminal.

Tagged: AI · All topics
~~~

Related posts about ai: