Skip to content

Architecture

pertmux uses a daemon/client architecture with Unix socket IPC.

┌─────────────────────────────────────────────────────────┐
│ pertmux serve │
│ (daemon) │
│ │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│ │ tmux │ │ Forge │ │ worktrunk│ │ agent │ │
│ │ poll │ │ API │ │ CLI │ │ status │ │
│ │ (2s) │ │ (60s) │ │ (30s) │ │ (2s) │ │
│ └────┬────┘ └────┬─────┘ └────┬─────┘ └────┬────┘ │
│ └─────────┬──┴─────────────┴──────────────┘ │
│ ▼ │
│ DashboardSnapshot │
│ │ │
│ Unix socket │ /tmp/pertmux-{USER}.sock │
└─────────────────┼───────────────────────────────────────┘
│ broadcast (multi-client)
┌─────────┼─────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ connect │ │ connect │ │ connect │
│ (TUI) │ │ (TUI) │ │ (TUI) │
└─────────┘ └─────────┘ └─────────┘

The daemon (pertmux serve) runs persistently in the background. It:

  • Owns all data state (panes, projects, MRs, worktrees)
  • Runs tiered refresh intervals: tmux/agent every 2s, worktrees every 30s, MR details every 60s
  • Listens on /tmp/pertmux-{USER}.sock
  • Broadcasts DashboardSnapshot to all connected clients via tokio::sync::broadcast
  • Processes client commands (refresh, worktree actions, etc.)
  • Processes CodexHook messages from pertmux codex-hook for immediate Codex status hints

The TUI client (pertmux connect) is lightweight. It:

  • Owns only UI state (selection indices, popup state, notifications)
  • Connects to the daemon via Unix socket
  • Receives DashboardSnapshot updates and renders them
  • Sends commands back to the daemon for data operations
  • Navigation is instant with no daemon round-trip

Communication uses LengthDelimitedCodec framing with serde_json serialization:

  • DashboardSnapshot: Full state snapshot sent from daemon to client
  • ClientMsg: Commands from client to daemon (Refresh, CreateWorktree, etc.)
  • DaemonMsg: Responses and snapshots from daemon to client
  • PROTOCOL_VERSION: Handshake validation on connect

All refresh intervals are configurable in the TOML config file.

DataIntervalTrigger
tmux panes + agent status2 secondsTimer
Worktrees5 minutesTimer
MR details60 secondsTimer
MR list300 secondsTimer + manual (r key)

Codex hooks are an event-driven fast path layered on top of the tmux/agent polling interval. UserPromptSubmit marks the matching Codex pane Busy, Stop marks it Idle, and hook-derived status is prioritized over the SQLite polling heuristic for that Codex session. The regular polling path continues to refresh metadata from Codex’s local SQLite databases.

The daemon also compares the active config file contents once per second. It parses and validates changed contents before rebuilding application state and resetting the tiered timers. Failed edits leave the previous application state and timers running.

PathPurpose
/tmp/pertmux-{USER}.sockDaemon Unix socket
/tmp/pertmux-daemon.logDaemon log file
<data directory>/pertmux/read_state.dbComment read/unread tracking and persistent agent activity cards
<data directory>/pertmux/last_projectLast selected project persistence

The data directory is ~/Library/Application Support on macOS. On Linux it is $XDG_DATA_HOME, which defaults to ~/.local/share.