# How SwarmForge Isolates tmux Sessions Using Custom Socket Paths

> Learn how SwarmForge isolates tmux sessions with custom socket paths, ensuring complete separation for every run. Discover the `-S <socket-path>` flag in action.

- Repository: [Robert C. Martin/swarm-forge](https://github.com/unclebob/swarm-forge)
- Tags: internals
- Published: 2026-08-30

---

**SwarmForge isolates tmux sessions by generating a unique Unix socket file per run and passing the `-S <socket-path>` flag to every tmux command, ensuring complete separation from any existing tmux servers on the host.**

SwarmForge is a role-based orchestration tool for managing AI coding agents. According to the unclebob/swarm-forge source code, it achieves **tmux session isolation** through a carefully designed socket-based architecture that prevents interference with users' personal tmux configurations or other running projects.

## How Socket Path Isolation Works in SwarmForge

SwarmForge's isolation strategy centers on three core mechanisms: generating unique socket identifiers, persisting socket paths for reuse, and scoping every tmux invocation to that private socket.

### Generating Unique Socket Identifiers

When SwarmForge initializes its **context**, it creates a deterministic yet unique socket file name using a **CRC-32 checksum** of the absolute working directory. This ensures the same project always maps to the same socket, while different projects remain isolated.

The socket directory follows a **portable temporary path pattern** under `/tmp` to avoid platform-specific issues:

```clojure
;; swarmforge/scripts/swarmforge.bb – lines 667-670
(let [socket-id (str (.getValue crc))
      tmux-socket-dir (fs/path "/tmp"
                        (str "swarmforge-" (or (System/getenv "UID")
                                              (System/getProperty "user.name"))))
      tmux-socket (str (fs/path tmux-socket-dir (str socket-id ".sock")))]

```

The `UID` environment variable takes precedence, falling back to `user.name` for broader compatibility. This produces paths like `/tmp/swarmforge-joe/1234567890.sock`.

### Persisting the Socket Path

SwarmForge writes the computed socket path to `.swarmforge/tmux-socket` within the project root. All subsequent operations—including helper scripts and watchdog processes—read this file to locate the correct socket:

```clojure
;; swarmforge/scripts/swarmforge.bb – line 316
(spit (str (:tmux-socket-file ctx)) (str (:tmux-socket ctx) "\n"))

```

This persistence enables **inter-process coordination** without hardcoding paths or relying on environment variable inheritance.

### Creating Sessions on the Custom Socket

Every role's tmux session is bootstrapped with explicit `-S` flags. The `create-role-session!` function in `swarmforge.bb` demonstrates this pattern:

```clojure
;; swarmforge/scripts/swarmforge.bb – lines 822-836
(defn create-role-session! [ctx session title]
  (sh "tmux" "-S" (:tmux-socket ctx) "new-session" "-d" "-s" session "-n" agent-window)
  (sh "tmux" "-S" (:tmux-socket ctx) "set-option" "-t" session "history-limit" (str pane-history-limit))
  (sh "tmux" "-S" (:tmux-socket ctx) "rename-window" "-t" (str session ":" agent-window) title)
  (sh "tmux" "-S" (:tmux-socket ctx) "set-window-option"
      "-t" (str session ":" title) "allow-rename" "off"))

```

Each `sh` invocation includes `-S` followed by the socket path, directing commands to SwarmForge's private tmux server rather than the default socket.

### Scoping All tmux Queries and Control

Helper functions in `swarm_window_watchdog.bb` extend isolation to **session introspection and cleanup**:

```clojure
;; swarmforge/scripts/swarm_window_watchdog.bb – lines 54-55
(defn tmux-session? [tmux-socket session]
  (zero? (:exit (process/sh {:continue true}
               "tmux" "-S" tmux-socket "has-session" "-t" session))))

```

Similarly, `kill-session!` and `send-keys!` operations all carry the `-S` flag. This guarantees that checking for session existence or terminating a role never affects unrelated tmux servers.

## Verifying Portable Socket Directories

The SwarmForge test suite explicitly validates that socket paths remain **platform-agnostic**, particularly avoiding macOS's private `/tmp` alias:

```clojure
;; test/swarmforge/script_test.clj – lines 124-128
(deftest swarmforge-uses-portable-tmux-socket-dir
  (let [socket-path (str/trim (slurp (str (fs/path root ".swarmforge/tmux-socket"))))]
    (is (str/starts-with? socket-path "/tmp/swarmforge-"))
    (is (not (str/starts-with? socket-path "/private/tmp/")))))

```

This test prevents regression on macOS, where `/tmp` symlinks to `/private/tmp` and some tmux operations behave differently with resolving versus non-resolving paths.

## Manual Re-attachment to Isolated Sessions

Users can interact directly with SwarmForge's isolated tmux server using the printed socket path:

```bash

# Start SwarmForge (socket created automatically)

$ ./swarmforge.sh /path/to/project

# Output includes:

#   Tip: Reattach manually with 'tmux -S /tmp/swarmforge-joe/12345.sock attach-session -t swarmforge-coder'

# Manual operations using the custom socket

$ tmux -S /tmp/swarmforge-joe/12345.sock attach-session -t swarmforge-coder
$ tmux -S /tmp/swarmforge-joe/12345.sock send-keys -t swarmforge-coder:0 "git status" C-m
$ tmux -S /tmp/swarmforge-joe/12345.sock kill-session -t swarmforge-coder

```

The `-S` flag must precede all tmux subcommands. Omitting it connects to the default tmux server, which contains no SwarmForge sessions.

## Key Implementation Files

| File | Responsibility |
|------|---------------|
| `swarmforge/scripts/swarmforge.bb` | Socket generation, context initialization, session creation |
| `swarmforge/scripts/swarm_window_watchdog.bb` | Session queries and lifecycle helpers with `-S` scoping |
| `test/swarmforge/script_test.clj` | Assertions for portable `/tmp/swarmforge-*` paths |
| `swarmforge/scripts/terminal-adapters/*.sh` | Terminal-specific wrappers passing `TMUX_SOCKET` environment variable |
| `swarmforge/scripts/pack_*.bb` | Role-specific scripts that read `.swarmforge/tmux-socket` and send keys |

## Summary

SwarmForge isolates tmux sessions through a **custom socket path architecture**:

- **Unique socket generation**: CRC-32 of working directory + user-specific `/tmp` directory
- **Path persistence**: Stored in `.swarmforge/tmux-socket` for cross-script coordination
- **Universal `-S` flagging**: Every tmux command targets the private socket
- **Platform portability**: Explicitly tested to avoid macOS `/private/tmp` issues
- **User transparency**: Socket path printed for manual re-attachment when needed

This design ensures multiple SwarmForge projects—and personal tmux usage—can coexist without conflict on the same host.

## Frequently Asked Questions

### What happens if I run SwarmForge from the same directory twice?

SwarmForge computes the **same socket path** because the CRC-32 depends only on the absolute working directory. The second invocation connects to the existing tmux server rather than creating a conflict, though role session names must be unique to avoid collisions.

### Can I change where SwarmForge creates its socket files?

No—the socket directory is hardcoded to `/tmp/swarmforge-<user>` in `swarmforge.bb`. This limitation ensures the test suite's portability guarantees remain valid. Modify the source at lines 667-670 if you require a custom location.

### Why does SwarmForge use CRC-32 instead of random identifiers?

**Deterministic socket paths** enable reconnection to an existing SwarmForge instance if the controlling process restarts. A random UUID would orphan running tmux servers and prevent recovery of in-flight agent sessions.

### How do I troubleshoot "no server running on <socket>" errors?

Verify the socket file exists at the path printed in the startup tip. If missing, SwarmForge may have crashed during initialization or cleaned up on exit. Check permissions on `/tmp/swarmforge-<user>/` and ensure no other user owns the directory.