vibetunnel/web/spec.md

20 KiB

VibeTunnel Codebase Map

A comprehensive navigation guide for the VibeTunnel web terminal system.

Project Overview

VibeTunnel is a web-based terminal multiplexer with distributed architecture support. It provides:

  • PTY-based terminal sessions via node-pty
  • Real-time terminal streaming via SSE (asciinema cast files)
  • Binary-optimized buffer synchronization (current viewport via WebSocket)
  • Distributed HQ/remote server architecture
  • Web UI with full terminal emulation

Directory Structure

web/
├── src/
│   ├── server/           # Node.js Express server
│   │   ├── middleware/   # Auth and other middleware
│   │   ├── pty/         # PTY management
│   │   ├── routes/      # API endpoints
│   │   ├── services/    # Core services
│   │   ├── server.ts    # Server loader
│   │   └── fwd.ts       # CLI forwarding tool
│   ├── client/           # Lit-based web UI
│   │   ├── assets/      # Static files (fonts, icons, html)
│   │   ├── components/  # UI components
│   │   ├── services/    # Client services
│   │   └── utils/       # Client utilities
│   ├── test/            # Test files
│   └── index.ts         # Main entry point
├── scripts/             # Build scripts
└── public/              # Built static assets (generated)

Server Architecture (src/server/)

Core Components

Entry Points

  • index.ts (1-25): Main entry point, chooses between server and forward modes
  • server/server.ts (1-4): Simple loader for modular server
  • server/index.ts (1-49): Server initialization, cleanup intervals, graceful shutdown
  • server/app.ts (198-404): Express app factory, CLI parsing, mode configuration

Server Modes

  1. Normal Mode: Standalone terminal server
  2. HQ Mode (--hq): Central server managing remotes
  3. Remote Mode (--hq-url): Registers with HQ server

Authentication (middleware/auth.ts)

  • Basic Auth: Username/password (46-55)
  • Bearer Token: HQ↔Remote communication (28-44)
  • Health endpoint bypass (14-16)

Session Management

PTY Manager (pty/pty-manager.ts)

  • createSession() (155-287): Spawns PTY processes
    • Supports forwardToStdout option for direct stdout forwarding
    • Supports onExit callback for handling process termination
    • Automatic alias resolution: Uses ProcessUtils.resolveCommand() to detect and run aliases through shell
  • sendInput() (515-588): Handles keyboard input
  • killSession() (687-764): SIGTERM→SIGKILL escalation
  • resizeSession() (638-681): Terminal dimension changes
  • Control pipe support using file watching on all platforms (414-475)
  • shutdown() (937-974): Clean termination of all active sessions
  • Proper TypeScript types throughout (no "as any" assertions)

Process Utils (pty/process-utils.ts)

  • resolveCommand() (168-242): Detects if command exists in PATH or needs shell execution
    • Uses which (Unix) or where (Windows) to check command existence
    • Returns appropriate shell command with args for aliases/builtins
    • Platform-specific shell argument handling
  • getUserShell() (219-281): Determines user's preferred shell
    • Checks $SHELL environment variable first
    • Windows: Checks for pwsh, PowerShell, Git Bash, then cmd.exe
    • Unix: Checks common shell paths (/bin/zsh, /bin/bash, etc.)

Session Manager (pty/session-manager.ts)

  • Session persistence in ~/.vibetunnel/control/
  • listSessions() (155-224): Filesystem-based discovery
  • updateZombieSessions() (231-257): Dead process cleanup

Terminal Manager (services/terminal-manager.ts)

  • Headless xterm.js for server-side state (40-69)
  • getBufferSnapshot() (255-323): Captures terminal buffer
  • encodeSnapshot() (328-555): Binary protocol encoding
  • Debounced buffer notifications (627-642)

API Routes (routes/)

Sessions (sessions.ts)

  • GET /api/sessions (40-120): List with HQ aggregation
    • Returns array of SessionEntryWithId objects with additional fields:
      • All fields from SessionEntryWithId (see types.ts)
      • source: 'local' | 'remote'
      • For remote sessions: remoteId, remoteName, remoteUrl
  • POST /api/sessions (123-199): Create local/remote
  • DELETE /api/sessions/:id (270-323): Kill session
  • GET /api/sessions/:id/stream (517-627): SSE streaming of asciinema cast files
  • POST /api/sessions/:id/input (630-695): Send input
  • POST /api/sessions/:id/resize (698-767): Resize terminal
  • GET /api/sessions/:id/buffer (569-631): Binary snapshot of current terminal view
  • GET /api/sessions/:id/text (504-654): Plain text of current terminal view
    • Optional ?styles query parameter adds style markup
    • Style format: [style fg="color" bg="color" bold italic ...]text[/style]
    • Colors: indexed (0-255) as "15", RGB as "255,128,0"
    • Attributes: bold, dim, italic, underline, inverse, invisible, strikethrough
  • GET /api/sessions/activity (255-311): Activity status for all sessions
    • Returns: { [sessionId]: ActivityStatus } where ActivityStatus includes:
      • isActive: boolean - Currently generating output
      • timestamp: string - Last update time
      • session: SessionInfo object (see types.ts)
    • In HQ mode: aggregates activity from all remote servers
  • GET /api/sessions/:id/activity (314-370): Activity status for specific session
    • Returns: ActivityStatus object (same format as above)
    • In HQ mode: forwards to appropriate remote server

Remotes (remotes.ts) - HQ Mode Only

  • GET /api/remotes (15-27): List registered servers
  • POST /api/remotes/register (30-52): Remote registration
  • DELETE /api/remotes/:id (55-69): Unregister remote

Logs (logs.ts)

  • POST /api/logs/client (24-56): Client-side log submission
    • Accepts: { level, module, args }
    • Prefixes module with CLIENT: for identification
  • GET /api/logs/raw (59-76): Stream raw log file
  • GET /api/logs/info (79-104): Log file metadata
  • DELETE /api/logs/clear (107-121): Clear log file

Binary Buffer Protocol

Note: "Buffer" refers to the current terminal display state (visible viewport) without scrollback history - just what's currently shown at the bottom of the terminal. This is used for rendering terminal previews in the session list.

Format (terminal-manager.ts:361-555)

Header (32 bytes):
- Magic: 0x5654 "VT" (2 bytes)
- Version: 0x01 (1 byte)
- Flags: reserved (1 byte)
- Dimensions: cols, rows (8 bytes)
- Cursor: X, Y, viewport (12 bytes)
- Reserved (4 bytes)

Rows: 0xFE=empty, 0xFD=content
Cells: Variable-length with type byte

SSE Streaming and Asciinema Files

File Writing (pty/asciinema-writer.ts)

  • AsciinemaWriter (separate file) writes cast files to ~/.vibetunnel/control/[sessionId]/stream-out
  • Used by PtyManager when creating sessions
  • Records in asciinema v2 format with custom extensions:
    • Standard: [timestamp, "o", output] for terminal output
    • Standard: [timestamp, "i", input] for user input
    • Standard: [timestamp, "r", "colsxrows"] for resize events
    • Custom: ["exit", exitCode, sessionId] when process terminates

SSE Streaming (routes/sessions.ts:517-627)

  • Streams asciinema cast files from disk in real-time
  • StreamWatcher monitors file changes and broadcasts to clients
  • Replays existing content first (timestamps zeroed)
  • Watches for new content and streams incrementally
  • Closes connections on exit event

Client Playback (client/utils/cast-converter.ts)

  • Parses asciinema v2 format
  • Handles custom exit event to dispatch session-exit
  • Supports batch loading and real-time streaming

WebSocket (services/buffer-aggregator.ts)

  • Client connections (31-68)
  • Message handling (69-127)
  • Local session buffers (131-195)
  • Remote session proxy (200-232)
  • Binary message format (136-164)

Activity Monitoring (services/activity-monitor.ts)

Overview

Monitors all terminal sessions for activity by watching stream-out file changes.

  • Works for ALL sessions regardless of creation method (server or fwd.ts)
  • No performance impact on terminal output
  • File-based persistence for cross-process access

Implementation

  • start() (26-37): Begins monitoring with 100ms check interval
  • scanSessions() (61-98): Discovers and monitors session directories
  • handleFileChange() (146-168): Detects output by file size increase
  • updateActivityStates() (173-182): Marks inactive after 500ms timeout
  • writeActivityStatus() (187-198): Persists to activity.json per session

Activity Status Format

{
  "isActive": boolean,
  "timestamp": "ISO 8601 date string",
  "session": {                                   // SessionInfo object from session.json
    "cmdline": ["command", "args"],
    "name": "session name",
    "cwd": "/working/directory",
    "pid": 12345,
    "status": "starting" | "running" | "exited",
    "exit_code": 0,
    "started_at": "ISO 8601 date string",
    "term": "xterm-256color",
    "spawn_type": "pty"
  }
}

HQ Mode Components

Remote Registry (services/remote-registry.ts)

  • Health checks every 15s (118-146)
  • Session ownership tracking (82-96)
  • Bearer token authentication

HQ Client (services/hq-client.ts)

  • Registration with HQ (29-58)
  • Unregister on shutdown (60-72)

Logging Infrastructure (utils/logger.ts)

Server Logger

  • Unified logging with file and console output
  • Log levels: log, warn, error, debug
  • File output to ~/.vibetunnel/log.txt
  • Formatted timestamps and module names
  • Debug mode toggle
  • Style guide compliance (see LOGGING_STYLE_GUIDE.md)

Client Logger (client/utils/logger.ts)

  • Mirrors server logger interface
  • Logs to browser console
  • Sends logs to /api/logs/client endpoint
  • Objects formatted as JSON before sending
  • Integrates with server logging system

Client Architecture (src/client/)

Core Components

Entry Points

  • app-entry.ts - Main application entry point
  • test-terminals-entry.ts - Test terminals entry point
  • styles.css - Global styles

Main Application Component

  • app.ts - Lit-based SPA (15-331)
    • URL-based routing ?session=<id>
    • Global keyboard handlers
    • Error/success message handling (74-90)
    • Events fired:
      • toggle-nav - Toggle navigation
      • navigate-to-list - Navigate to session list
      • error - Display error message
      • success - Display success message
      • navigate - Navigate to specific session
    • Events listened: Various events from child components

Component Event Architecture

Terminal Components

terminal.ts - Custom DOM terminal renderer (17-1000+)

Full terminal implementation with xterm.js for rendering and input handling.

  • Virtual scrolling (537-555)
  • Touch/momentum support
  • URL highlighting integration
  • Copy/paste handling
  • Events fired:
    • terminal-ready - When terminal is initialized and ready
    • terminal-input - When user types (detail: string)
    • terminal-resize - When terminal is resized (detail: { cols: number, rows: number })
    • url-clicked - When a URL is clicked (detail: string)
session-view.ts - Full-screen terminal view (29-1331)

Full-screen terminal view for an active session. Handles terminal I/O, streaming updates via SSE, file browser integration, and mobile overlays.

  • SSE streaming (275-333)
  • Mobile input overlays
  • Resize synchronization
  • Events fired:
    • navigate-to-list - When navigating back to session list
    • error - When an error occurs (detail: string)
    • warning - When a warning occurs (detail: string)
  • Events listened:
    • session-exit - From SSE stream when session exits
    • terminal-ready - From terminal component when ready
    • file-selected - From file browser when file is selected
    • browser-cancel - From file browser when cancelled
vibe-terminal-buffer.ts - Terminal buffer display (25-268)

Displays a read-only terminal buffer snapshot with automatic resizing. Subscribes to buffer updates via WebSocket and renders the terminal content.

  • Events fired:
    • content-changed - When terminal content changes (no detail)

Session Management Components

session-list.ts - Active sessions list view (61-700+)

Main session list view showing all active terminal sessions with real-time updates, search/filtering, and session management capabilities.

  • Events fired:
    • navigate - When clicking on a session (detail: { sessionId: string })
    • error - When an error occurs (detail: string)
    • success - When an operation succeeds (detail: string)
    • session-created - When a new session is created (detail: Session)
    • session-updated - When a session is updated (detail: Session)
    • sessions-changed - When the session list changes
    • toggle-create-form - When toggling the create form
  • Events listened:
    • session-created - From create form
    • cancel - From create form
    • error - From create form
session-card.ts - Individual session card (31-420+)

Individual session card component showing terminal preview and session controls. Displays a live terminal buffer preview and detects activity changes.

  • Events fired:
    • view-session - When viewing a session (detail: Session)
    • kill-session - When killing a session (detail: Session)
    • copy-session-id - When copying session ID (detail: Session)
  • Events listened:
    • content-changed - From vibe-terminal-buffer component
session-create-form.ts - New session creation form (27-381)

Modal dialog for creating new terminal sessions. Provides command input, working directory selection, and options for spawning in native terminal.

  • Events fired:
    • session-created - When session is successfully created (detail: { sessionId: string, message?: string })
    • cancel - When form is cancelled
    • error - When creation fails (detail: string)
  • Events listened:
    • file-selected - From file browser when directory is selected
    • browser-cancel - From file browser when cancelled

UI Components

app-header.ts - Application header (15-280+)

Main application header with logo, title, navigation controls, and session status.

  • Events fired:
    • toggle-nav - Toggle navigation menu
    • navigate-to-list - Navigate to session list
    • toggle-create-form - Toggle session create form
    • toggle-theme - Toggle dark/light theme
    • open-settings - Open settings modal
file-browser.ts - File browser component (48-665)

Modal file browser for navigating the filesystem and selecting files/directories. Supports Git status display, file preview with Monaco editor, and diff viewing.

  • Events fired:
    • insert-path - When inserting a file path into terminal (detail: { path: string, type: 'file' | 'directory' })
    • open-in-editor - When opening a file in external editor (detail: { path: string })
    • directory-selected - When a directory is selected in 'select' mode (detail: string)
    • browser-cancel - When the browser is cancelled or closed
log-viewer.ts - System log viewer (1-432)

Real-time log viewer with filtering and search capabilities.

  • SSE-style polling every 2 seconds
  • Client/server log distinction
  • Log level filtering
  • Relative timestamps
  • Mobile-responsive layout
  • Mac-style auto-hiding scrollbars
  • Features:
    • Filter by log level (error, warn, log, debug)
    • Toggle client/server logs
    • Search/filter by text
    • Auto-scroll (smart - only when near bottom)
    • Download logs
    • Clear logs
Icon Components
  • vibe-logo.ts - Application logo
  • terminal-icon.ts - Terminal icon
  • copy-icon.ts - Copy icon

Services

Buffer Subscription (services/buffer-subscription-service.ts)

  • WebSocket client (30-87)
  • Binary protocol decoder (163-208)
  • Auto-reconnection with backoff

Utils

Cast Converter (utils/cast-converter.ts)

  • Asciinema v2 parser (31-82)
  • SSE stream handler (294-427)
  • Batch loading (221-283)

Terminal Renderer (utils/terminal-renderer.ts)

  • Binary buffer decoder (279-424)
  • HTML generation
  • Style mapping

Additional Utilities

  • url-highlighter.ts - URL detection and highlighting
  • xterm-colors.ts - Terminal color definitions
  • terminal-preferences.ts - Terminal preference management

Forward Tool (src/server/fwd.ts)

Purpose

Simplified CLI tool that spawns PTY sessions using VibeTunnel infrastructure.

Key Features

  • Interactive terminal forwarding with colorful output (chalk)
  • Session creation with pre-generated IDs support
  • Graceful cleanup on exit
  • Automatic shell alias support via ProcessUtils.resolveCommand()

Usage

npx tsx src/fwd.ts [--session-id <id>] <command> [args...]

# Examples with aliases
npx tsx src/fwd.ts claude-danger  # Automatically resolved through shell

Integration Points

  • Uses PtyManager for all session management
  • Control pipe and stdin forwarding handled by PtyManager
  • Automatic cleanup via PtyManager's shutdown() method

Key Files Quick Reference

Server Core

  • src/index.ts: Main entry point
  • src/server/server.ts: Server loader
  • src/server/app.ts: App configuration, CLI parsing
  • src/server/middleware/auth.ts: Authentication logic
  • src/server/routes/sessions.ts: Session API endpoints
  • src/server/pty/pty-manager.ts: PTY process management with file-watching control pipes
  • src/server/pty/asciinema-writer.ts: Cast file writer
  • src/server/services/terminal-manager.ts: Terminal state & binary protocol
  • src/server/services/buffer-aggregator.ts: WebSocket buffer distribution
  • src/server/services/stream-watcher.ts: SSE file streaming
  • src/server/services/activity-monitor.ts: Session activity detection
  • src/server/fwd.ts: Simplified CLI forwarding tool

Client Core

  • src/client/app-entry.ts: Application entry point
  • src/client/app.ts: Main SPA component
  • src/client/components/terminal.ts: Terminal renderer
  • src/client/components/session-view.ts: Session viewer
  • src/client/components/session-list.ts: Session list view
  • src/client/services/buffer-subscription-service.ts: WebSocket client
  • src/client/utils/cast-converter.ts: Asciinema parser
  • src/client/assets/: Static files (fonts, icons, HTML)

Configuration

  • Environment: PORT, VIBETUNNEL_USERNAME, VIBETUNNEL_PASSWORD, VIBETUNNEL_DEBUG
  • CLI: --port, --username, --password, --hq, --hq-url, --name
  • Express static: .html extension handling for clean URLs
  • Debug logging: Set VIBETUNNEL_DEBUG=1 or VIBETUNNEL_DEBUG=true

Protocols

  • REST API: Session CRUD, terminal I/O, activity status
  • SSE: Real-time streaming of asciinema cast files from disk
  • WebSocket: Binary buffer updates (current terminal viewport)
  • Control pipes: External session control

Session Data Storage

Each session has a directory in ~/.vibetunnel/control/[sessionId]/ containing:

  • session.json: Session metadata
  • stream-out: Asciinema cast file with terminal output
  • stdin: Input pipe for sending keystrokes
  • control: Control pipe for resize/kill commands
  • activity.json: Activity status (written by ActivityMonitor)

Development Notes

Architecture Changes (Recent)

  • PTY Manager now uses file watching for control pipes on all platforms (not just FIFO)
  • No global exit handlers - clean shutdown via shutdown() method
  • Simplified fwd.ts - control pipe and stdin forwarding handled by PTY Manager
  • Added proper TypeScript types throughout (removed all "as any" assertions)
  • Cleaned up logging and added colorful output messages using chalk
  • Unified logging infrastructure:
    • Server-wide adoption of structured logger
    • Client-side logger with server integration
    • Centralized log viewer at /logs
    • Consistent style guide (LOGGING_STYLE_GUIDE.md)
  • Express enhancements:
    • Auto .html extension resolution for static files

Build System

  • pnpm run dev: Auto-rebuilds TypeScript
  • pnpm run build: Full build including Node.js SEA executable
  • ESBuild: Fast bundling
  • Node.js SEA: Creates standalone executable (Node.js 20+ required)
  • Vitest: Testing framework
  • Assets: Copied from src/client/assets/ to public/ during build

Testing

  • Unit tests: pnpm test
  • E2E tests: pnpm run test:e2e
  • Integration: pnpm run test:integration

Key Dependencies

  • node-pty: Cross-platform PTY
  • @xterm/headless: Terminal emulation
  • Lit: Web components
  • Express: HTTP server
  • TailwindCSS: Styling