# Five Ideas to Keep in Your Head

> The handful of concepts — workspace, surface, socket, notification, panel — that unlock every other part of the codebase. Understanding these five terms means understanding how cmux hangs together.

- Repository: manaflow-ai/cmux
- GitHub: https://github.com/manaflow-ai/cmux
- Human wiki: https://grok-wiki.com/public/wiki/manaflow-ai-cmux-e789cf316a9a
- Complete Markdown: https://grok-wiki.com/public/wiki/manaflow-ai-cmux-e789cf316a9a/llms-full.txt

## Source Files

- `Sources/Workspace.swift`
- `Sources/TabManager.swift`
- `Sources/SocketControlSettings.swift`
- `Sources/Panels/Panel.swift`
- `Sources/TerminalNotificationStore.swift`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:

- [Sources/Workspace.swift](Sources/Workspace.swift)
- [Sources/TabManager.swift](Sources/TabManager.swift)
- [Sources/SocketControlSettings.swift](Sources/SocketControlSettings.swift)
- [Sources/Panels/Panel.swift](Sources/Panels/Panel.swift)
- [Sources/TerminalNotificationStore.swift](Sources/TerminalNotificationStore.swift)
- [Sources/Panels/TerminalPanel.swift](Sources/Panels/TerminalPanel.swift)
- [Sources/GhosttyTerminalView.swift](Sources/GhosttyTerminalView.swift)
</details>

# Five Ideas to Keep in Your Head

cmux is a macOS terminal multiplexer built on top of the Ghostty terminal engine. Its source is large — Workspace.swift alone exceeds 680 KB — but almost everything you encounter in the codebase is a variation on five vocabulary words. Once you know what *workspace*, *surface*, *socket*, *notification*, and *panel* mean in cmux's own terms, you have the skeleton key to every other file.

This page introduces each concept plainly, maps it to the Swift type that owns it, and shows how the five concepts fit together. Reading it once is enough to navigate the rest of the code without getting lost.

---

## 1. Workspace — the row in your sidebar

A **workspace** is one entry in the left-hand sidebar. Think of it as a named project slot. It holds a title, a custom color, a pinned flag, a current directory, and — most importantly — an arbitrarily complex split-pane layout of terminals and other panels.

The class is declared in `Sources/Workspace.swift`:

```swift
// Sources/Workspace.swift:8976-9001
final class Workspace: Identifiable, ObservableObject {
    let id: UUID
    @Published var title: String
    @Published var customTitle: String?
    @Published var isPinned: Bool = false
    @Published var currentDirectory: String
    @Published var panels: [UUID: any Panel] = [:]
    let bonsplitController: BonsplitController
    weak var owningTabManager: TabManager?
    ...
}
```

`TabManager.swift` keeps the compatibility alias `typealias Tab = Workspace` (line 12), so any older code that says "tab" still means the same thing.

A workspace is owned by a `TabManager`, which holds the ordered list you see in the sidebar:

```swift
// Sources/TabManager.swift:1106
@Published var tabs: [Workspace] = []
@Published var selectedTabId: UUID?
```

**Analogy:** a workspace is like a browser window's tab — except each "tab" can contain a whole grid of split panes, and the sidebar is your tab bar.

Sources: [Sources/Workspace.swift:8976-9053](), [Sources/TabManager.swift:1106-1127]()

---

## 2. Surface — the live Ghostty terminal instance

A **surface** is a single running terminal. Where a workspace is the container, the surface is the hot core: it holds the Ghostty C handle, renders text, and accepts keyboard input. You can have many surfaces inside one workspace (one per split pane).

The type is `TerminalSurface`, defined in `Sources/GhosttyTerminalView.swift`:

```swift
// Sources/GhosttyTerminalView.swift:5083
final class TerminalSurface: Identifiable, ObservableObject {
    ...
    enum InputSendResult: Equatable {
        case sent
        case queued
        case inputQueueFull
        case surfaceUnavailable
        case processExited
    }
    ...
}
```

`TerminalSurface` wraps the low-level Ghostty surface handle. The socket command layer (see §3) routes keystrokes and paste events through a `PendingSocketInput` queue inside `TerminalSurface`. The queue exists so that commands arriving while a surface is busy (e.g., mid-resize) are held safely and replayed.

Surfaces are not directly stored in `Workspace.panels`; they are wrapped inside a `TerminalPanel` (see §5). The identifier `surfaceId` seen in notification and socket code is the UUID of the `TerminalPanel` that wraps a given surface.

Sources: [Sources/GhosttyTerminalView.swift:5083-5140](), [Sources/Panels/TerminalPanel.swift:1-29]()

---

## 3. Socket — the local automation door

A **socket** is a Unix domain socket that lets external processes (scripts, agents, the `cmux` CLI) send commands to the running app. It is the reason the CLAUDE.md includes dogfood patterns like `CMUX_SOCKET_PATH` — every CLI command goes through it.

`SocketControlSettings` (in `Sources/SocketControlSettings.swift`) owns the policy for who may connect:

```swift
// Sources/SocketControlSettings.swift:8-18
enum SocketControlMode: String, CaseIterable, Identifiable {
    case off
    case cmuxOnly       // only processes started inside cmux terminals
    case automation     // any process from the same macOS user
    case password       // requires a file-based password
    case allowAll       // no auth; unsafe
    ...
}
```

The default mode is `.cmuxOnly` — only processes whose parent chain started inside a cmux terminal can connect. File permissions reinforce this: modes other than `allowAll` set the socket to `0o600` (owner-only access).

Socket path resolution is non-trivial because debug, staging, and release builds must never share a socket (they would steal each other's commands). The rule is:

| Build variant | Socket path pattern |
|---|---|
| Release (`com.cmuxterm.app`) | `~/Library/Application Support/cmux/cmux.sock` |
| Debug untagged | Blocked at launch — must pass `CMUX_TAG` |
| Debug tagged | `/tmp/cmux-debug-<tag>.sock` |
| Staging | Separate path, configurable |

The `socketPath(environment:bundleIdentifier:isDebugBuild:...)` function in `SocketControlSettings` encodes all of this logic and is tested extensively in `cmuxTests/`.

Sources: [Sources/SocketControlSettings.swift:8-62](), [Sources/SocketControlSettings.swift:293-460]()

---

## 4. Notification — the alert that travels through the stack

A **notification** in cmux means a terminal-originated alert: a long-running command finished, a monitored pattern matched output, etc. It is not a Swift `Notification` or `NotificationCenter` event — it is a domain object of type `TerminalNotification`.

```swift
// Sources/TerminalNotificationStore.swift:694-740
struct TerminalNotification: Identifiable, Hashable {
    let id: UUID
    let tabId: UUID       // workspace
    let surfaceId: UUID?  // which terminal pane triggered it
    let panelId: UUID?
    let title: String
    let subtitle: String
    let body: String
    let createdAt: Date
    var isRead: Bool
    var paneFlash: Bool   // whether to flash the panel ring
    var clickAction: TerminalNotificationClickAction?
}
```

`TerminalNotificationStore` (a `@MainActor` singleton) holds all live notifications and drives:

- **Dock badge** — the unread count badge on the app icon.
- **Sidebar badges** — per-workspace unread counts.
- **Panel flash / ring** — a brief visual pulse on the originating pane (`paneFlash`).
- **System notification** — a macOS `UNUserNotificationCenter` delivery when the app is not focused.
- **Custom sound or shell command** — optional hooks configured by the user.

The store suppresses external delivery (system notification + sound) when the originating workspace and surface are already focused and the app is in the foreground, playing only a local sound instead. Policy hooks (user-defined scripts) can transform or suppress a notification before it is recorded.

Sources: [Sources/TerminalNotificationStore.swift:694-740](), [Sources/TerminalNotificationStore.swift:742-815]()

---

## 5. Panel — the polymorphic pane occupant

A **panel** is whatever occupies one split-pane slot in a workspace. Every pane holds exactly one panel; one workspace can hold many panels. The word captures the fact that panes are not terminal-only — they can contain a terminal, an embedded browser, a Markdown preview, a file preview, or a sidebar tool.

`Panel` is a `@MainActor` protocol in `Sources/Panels/Panel.swift`:

```swift
// Sources/Panels/Panel.swift:259-307
@MainActor
public protocol Panel: AnyObject, Identifiable, ObservableObject where ID == UUID {
    var id: UUID { get }
    var panelType: PanelType { get }  // .terminal | .browser | .markdown | .filePreview | .rightSidebarTool
    var displayTitle: String { get }
    var isDirty: Bool { get }
    func close()
    func focus()
    func unfocus()
    func triggerFlash(reason: WorkspaceAttentionFlashReason)
    func captureFocusIntent(in window: NSWindow?) -> PanelFocusIntent
    func restoreFocusIntent(_ intent: PanelFocusIntent) -> Bool
    ...
}
```

The concrete implementations are:

| Type | File | `panelType` |
|---|---|---|
| `TerminalPanel` | `Sources/Panels/TerminalPanel.swift` | `.terminal` |
| `BrowserPanel` | `Sources/Panels/BrowserPanel.swift` | `.browser` |
| `MarkdownPanel` | `Sources/Panels/MarkdownPanel.swift` | `.markdown` |
| `FilePreviewPanel` | `Sources/Panels/FilePreviewPanel.swift` | `.filePreview` |

`TerminalPanel` is the most common. It wraps a `TerminalSurface` and bridges between the SwiftUI layout system and the Ghostty C layer:

```swift
// Sources/Panels/TerminalPanel.swift:9-23
@MainActor
final class TerminalPanel: Panel, ObservableObject {
    let id: UUID
    let panelType: PanelType = .terminal
    let surface: TerminalSurface
    private(set) var workspaceId: UUID
    ...
}
```

`Workspace.panels` is a `[UUID: any Panel]` dictionary. The key is the panel's own UUID, which is also the `surfaceId` the rest of the codebase uses when routing notifications, socket commands, and focus events.

Sources: [Sources/Panels/Panel.swift:259-307](), [Sources/Panels/TerminalPanel.swift:6-29](), [Sources/Workspace.swift:9031-9033]()

---

## How the Five Fit Together

```text
TabManager
  └─ [Workspace]  (sidebar rows, each has a UUID = "tabId")
       └─ [Panel]  (keyed by UUID = "panelId" / "surfaceId")
            └─ TerminalPanel → TerminalSurface  (live Ghostty renderer)
            └─ BrowserPanel, MarkdownPanel, ...

Socket  ──────────────────────────────────────────────────────►  commands routed to Workspace or TerminalSurface
                                                                   via surfaceId / tabId

TerminalNotificationStore  (singleton)
  ├─ per-tabId  (workspace badge)
  └─ per-surfaceId / panelId  (pane flash, macOS UNNotification)
```

A socket command arrives at the app, is dispatched by `tabId` to a `Workspace`, then by `surfaceId` to the correct `TerminalPanel` and its `TerminalSurface`. A notification follows the same addressing scheme in reverse: the terminal emits an event, `TerminalNotificationStore` records it with `tabId` and `surfaceId`, and then drives the sidebar badge, pane flash, and optional macOS system notification from that single store.

The five terms — workspace, surface, socket, notification, panel — are therefore the coordinate system for the entire event loop: every command, every display update, and every alert is identified by some combination of these IDs and routed through one of these types.

Sources: [Sources/TerminalNotificationStore.swift:1156-1168](), [Sources/Workspace.swift:9031-9065](), [Sources/SocketControlSettings.swift:421-460]()
