Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/jaypopat/cf_ai_duet/llms.txt

Use this file to discover all available pages before exploring further.

Duet is a real-time collaborative terminal sharing application built on Go with Charm’s SSH and TUI libraries. It enables multiple users to connect via SSH and share a live terminal session in synchronized rooms.

Core Architecture

Duet follows a client-server architecture where:
  1. SSH Server handles incoming connections using Charm’s Wish framework
  2. Room Manager orchestrates collaborative sessions with isolated workspaces
  3. Terminal Emulator provides shared PTY access with vt10x emulation
  4. Bubble Tea UI renders the interactive terminal interface for each client

System Components

┌─────────────────────────────────────────────────────────────┐
│                       SSH Clients                           │
│                    (ssh user@host:2222)                     │
└────────────┬────────────────────────────┬───────────────────┘
             │                            │
             ▼                            ▼
┌────────────────────────────────────────────────────────────┐
│                    Wish SSH Server                          │
│  ┌──────────────────┐  ┌──────────────────────────────┐   │
│  │ Bubbletea        │  │ Logging                      │   │
│  │ Middleware       │  │ Middleware                   │   │
│  └──────────────────┘  └──────────────────────────────┘   │
└────────────┬───────────────────────────────────────────────┘


┌────────────────────────────────────────────────────────────┐
│                     Room Manager                            │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐    │
│  │   Room A    │  │   Room B    │  │   Room C        │    │
│  │  (clients)  │  │  (clients)  │  │  (clients)      │    │
│  │  Terminal   │  │  Terminal   │  │  Terminal       │    │
│  │  Workspace  │  │  Workspace  │  │  Workspace      │    │
│  └─────────────┘  └─────────────┘  └─────────────────┘    │
└────────────────────────────────────────────────────────────┘


┌────────────────────────────────────────────────────────────┐
│              Shared Terminal (per room)                     │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │  PTY/Shell   │→ │ vt10x Parser │→ │ Subscribers  │     │
│  │  (creack/pty)│  │ (hinshun)    │  │ (broadcast)  │     │
│  └──────────────┘  └──────────────┘  └──────────────┘     │
└────────────────────────────────────────────────────────────┘

Data Flow

Connection Flow

  1. Client connects via SSH to the server on port 2222
  2. Wish middleware processes the connection and extracts session info
  3. Bubble Tea creates a new TUI model with the Room Manager reference
  4. User selects “Create Room” or “Join Room” from the launch screen
  5. Room Manager creates/retrieves the room and registers the client
  6. Terminal starts (or connects to existing shared terminal)
  7. Client subscribes to terminal updates via broadcast channel

Terminal Input/Output Flow

Client Keystroke

Bubble Tea Model (handleRoomKey)

terminal.Write(data) → PTY Master

Shell Process (executes command)

PTY Output → vt10x Parser

terminal.broadcast() → All Subscribers

Each Client's Update Loop

terminal.Render() → String Output

Bubble Tea View (displayed to client)

Room Event Broadcasting

Client Action (join/leave/type)

room.BroadcastEvent(event, excludeClientID)

For each client.Events channel (non-blocking send)

Model.listenForRoomEvents() receives event

Update UI (user list, toasts, typing indicators)

Key Design Patterns

Publisher-Subscriber Pattern

The terminal uses a pub-sub pattern for broadcasting updates to all connected clients:
type Terminal struct {
    subscribers map[chan struct{}]struct{}
    // ...
}

// Each client subscribes to terminal updates
ch := terminal.Subscribe()

// Terminal broadcasts when PTY output arrives
func (t *Terminal) broadcast() {
    for ch := range t.subscribers {
        select {
        case ch <- struct{}{}:
        default: // Non-blocking
        }
    }
}

Shared State with Mutex Protection

Both Room and Terminal use sync.RWMutex for thread-safe access:
type Room struct {
    Connections []*Client
    mu          sync.RWMutex
    // ...
}

func (r *Room) AddClient(client *Client) {
    r.mu.Lock()
    defer r.mu.Unlock()
    r.Connections = append(r.Connections, client)
}

Isolated Workspaces

Each room gets its own workspace directory:
workspaceDir := filepath.Join(baseDir, workspaceName)
cmd := exec.Command("cp", "-r", "/app/workspace-template/.", workspaceDir)
The terminal shell process runs with cmd.Dir = workspaceDir, providing file system isolation.

Technology Stack

Core Dependencies

  • SSH Server: github.com/charmbracelet/ssh - SSH protocol implementation
  • Wish: github.com/charmbracelet/wish - SSH middleware framework
  • Bubble Tea: github.com/charmbracelet/bubbletea - TUI framework
  • PTY: github.com/creack/pty - Pseudo-terminal interface
  • vt10x: github.com/hinshun/vt10x - Terminal emulator
  • Lipgloss: github.com/charmbracelet/lipgloss - Style definitions

Optional Integrations

  • AI Client: Cloudflare Workers AI integration for chat assistance
  • Sandbox: Remote command execution via worker
See the detailed component pages for implementation specifics:

Build docs developers (and LLMs) love