vibetunnel/web/spec.md
Mario Zechner 40d2cd1998
feat: add asciinema stream pruning for clear sequences (#155)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2025-07-01 05:01:59 +01:00

548 lines
No EOL
19 KiB
Markdown

# 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 v2 format)
- Binary-optimized buffer synchronization (current viewport via WebSocket)
- Distributed HQ/remote server architecture
- Web UI with full terminal emulation
- Push notifications for terminal bell events
- Multi-method authentication (SSH keys, JWT, PAM)
- Advanced terminal title management
## Directory Structure
```
web/
├── src/
│ ├── server/ # Node.js Express server
│ │ ├── middleware/ # Auth and other middleware
│ │ ├── pty/ # PTY management
│ │ ├── routes/ # API endpoints
│ │ ├── services/ # Core services
│ │ ├── utils/ # Server utilities
│ │ ├── server.ts # Main server implementation
│ │ └── 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
│ └── cli.ts # Main entry point
├── scripts/ # Build scripts
└── public/ # Built static assets (generated)
```
## Server Architecture (`src/server/`)
### Core Components
#### Entry Points
- `cli.ts:1-62`: Main entry point, routes to server or forward mode
- Version display, debug initialization, mode routing
- `server.ts:1-1017`: Core server implementation with Express app factory
- CLI parsing: `142-246`
- Server modes: `432-455` (Normal, HQ, Remote initialization)
- WebSocket upgrade: `585-707` (Authentication and buffer streaming)
- Graceful shutdown: `893-1017` (Cleanup intervals and service termination)
### Authentication (`middleware/auth.ts`)
- Multi-method authentication: `1-159`
- SSH key authentication with Ed25519 support
- Basic auth (username/password)
- Bearer token for HQ↔Remote communication
- JWT tokens for session persistence
- Local bypass for localhost connections: `24-48`, `68-87`
- Query parameter token support for EventSource
### Session Management
#### PTY Manager (`pty/pty-manager.ts`)
- Session creation: `173-400` (Spawns PTY processes with node-pty)
- **Automatic alias resolution**: `204-217` (Uses `ProcessUtils.resolveCommand()`)
- Terminal resize handling: `115-168` (Native terminal dimension sync)
- Control pipe support using file watching
- Bell event emission for push notifications
- Clean termination with SIGTERM→SIGKILL escalation
#### Process Utils (`pty/process-utils.ts`)
- `resolveCommand()`: `241-388` (Detects if command exists in PATH)
- Uses `which` (Unix) or `where` (Windows) to check existence
- Returns appropriate shell command for aliases/builtins
- Sources shell config files for proper alias support
- `getUserShell()`: `394-484` (Determines user's preferred shell)
- Checks `$SHELL` environment variable first
- Platform-specific fallbacks (pwsh/cmd on Windows, zsh/bash on Unix)
- Interactive shell detection: `220-235` (Auto-adds `-i -l` flags)
#### Session Manager (`pty/session-manager.ts`)
- Session persistence in `~/.vibetunnel/control/`
- Filesystem-based session discovery
- Zombie session cleanup: `337-366`
- Atomic writes for session metadata: `99-115`
#### Terminal Manager (`services/terminal-manager.ts`)
- Headless xterm.js for server-side state: `47-76`
- Binary buffer snapshot generation: `261-292`
- Watches asciinema cast files and applies to terminal
- Debounced buffer change notifications (100ms)
### API Routes (`routes/`)
#### Sessions (`sessions.ts`)
- `GET /api/sessions`: `51-124` - List all sessions
- Returns array with `source: 'local' | 'remote'`
- HQ mode: Aggregates from all remote servers
- `POST /api/sessions`: `126-265` - Create session
- Body: `{ command, workingDir?, name?, remoteId?, spawn_terminal?, cols?, rows?, titleMode? }`
- Returns: `{ sessionId: string, message?: string }`
- `GET /api/sessions/:id`: `369-410` - Get session info
- `DELETE /api/sessions/:id`: `413-467` - Kill session
- `DELETE /api/sessions/:id/cleanup`: `470-518` - Clean session files
- `POST /api/cleanup-exited`: `521-598` - Clean all exited sessions
#### Session I/O
- `POST /api/sessions/:id/input`: `874-950` - Send keyboard input
- Body: `{ text: string }` OR `{ key: SpecialKey }`
- `POST /api/sessions/:id/resize`: `953-1025` - Resize terminal
- Body: `{ cols: number, rows: number }`
- `POST /api/sessions/:id/reset-size`: `1028-1083` - Reset to native size
#### Session Output
- `GET /api/sessions/:id/stream`: `723-871` - SSE streaming
- Streams asciinema v2 format with custom exit event
- Replays existing content, then real-time streaming
- `GET /api/sessions/:id/buffer`: `662-721` - Binary buffer snapshot
- `GET /api/sessions/:id/text`: `601-659` - Plain text output
- Optional `?styles` for markup: `[style fg="15" bold]text[/style]`
#### Activity Monitoring
- `GET /api/sessions/activity`: `268-324` - All sessions activity
- `GET /api/sessions/:id/activity`: `327-366` - Single session activity
- Returns: `{ isActive: boolean, timestamp: string, session: SessionInfo }`
#### WebSocket Input (`routes/websocket-input.ts`)
- `GET /ws/input?sessionId=<id>&token=<token>`: Low-latency input
- Fire-and-forget protocol for keyboard/mouse input
- Direct PTY forwarding for performance
#### Remotes (`remotes.ts`) - HQ Mode Only
- `GET /api/remotes`: `19-33` - List registered servers
- `POST /api/remotes/register`: `36-64` - Register remote
- `DELETE /api/remotes/:id`: `67-84` - Unregister remote
- `POST /api/remotes/:id/refresh-sessions`: `87-152` - Refresh session list
#### Filesystem (`filesystem.ts`)
- `GET /api/fs/browse`: Browse directory with Git status
- `GET /api/fs/preview`: File preview with Monaco support
- `GET /api/fs/content`: Text file content
- `GET /api/fs/diff`: Git diff for files
- `POST /api/fs/mkdir`: Create directory
#### Logs (`logs.ts`)
- `POST /api/logs/client`: `21-53` - Client log submission
- `GET /api/logs/raw`: `56-74` - Stream raw log file
- `GET /api/logs/info`: `77-102` - Log file metadata
- `DELETE /api/logs/clear`: `105-119` - Clear log file
#### Push Notifications (`push.ts`)
- `GET /api/push/vapid-public-key`: Get VAPID public key
- `POST /api/push/subscribe`: Subscribe to notifications
- `POST /api/push/unsubscribe`: Unsubscribe
- `POST /api/push/test`: Send test notification
- `GET /api/push/status`: Get service status
#### Authentication (`auth.ts`)
- `POST /api/auth/challenge`: Create SSH key auth challenge
- `POST /api/auth/ssh-key`: Authenticate with SSH key
- `POST /api/auth/password`: Authenticate with password
- `GET /api/auth/verify`: Verify authentication status
- `GET /api/auth/config`: Get auth configuration
- `GET /api/auth/avatar/:userId`: Get user avatar (macOS)
### Binary Buffer Protocol
**Note**: "Buffer" refers to the current terminal viewport without scrollback - used for terminal previews.
#### Format (`terminal-manager.ts:361-579`)
```
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
#### Asciinema Writer (`pty/asciinema-writer.ts`)
- Writes cast files to `~/.vibetunnel/control/[sessionId]/stream-out`
- Format:
- 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:723-871`)
- Real-time streaming of asciinema cast files
- Replays existing content with zeroed timestamps
- Watches for new content and streams incrementally
- Heartbeat every 30 seconds
### WebSocket (`services/buffer-aggregator.ts`)
- Client connections: `35-71` (Authentication and subscription)
- Message handling: `76-145` (Subscribe/unsubscribe/ping)
- Binary protocol: `156-185` - `[0xBF][ID Length][Session ID][Buffer Data]`
- Local and remote session proxy support
### Activity Monitoring (`services/activity-monitor.ts`)
- Monitors `stdout` file size changes: `143-147`
- 100ms check interval: `41-44`
- 500ms inactivity timeout: `209-212`
- Persists to `activity.json` per session: `220-245`
- Works for all sessions regardless of creation method
### HQ Mode Components
#### Remote Registry (`services/remote-registry.ts`)
- Health checks every 15s: `150-187`
- Session ownership tracking: `91-148`
- Bearer token authentication
- Automatic unhealthy remote removal
#### HQ Client (`services/hq-client.ts`)
- Registration with HQ: `40-90`
- Unique ID generation with UUID v4
- Graceful unregistration on shutdown: `92-113`
### Additional Services
#### Push Notifications (`services/push-notification-service.ts`)
- Web Push API integration: `64-363`
- Subscription management in `~/.vibetunnel/notifications/`
- Bell event notifications: `231-247`
- Automatic expired subscription cleanup
#### Bell Event Handler (`services/bell-event-handler.ts`)
- Processes terminal bell events: `59-182`
- Integrates with push notifications
- Includes process context in notifications
#### Authentication Service (`services/auth-service.ts`)
- SSH key authentication: `144-159`, `201-271`
- Ed25519 signature verification
- Challenge-response system
- Checks `~/.ssh/authorized_keys`
- Password authentication: `105-120`
- PAM authentication fallback: `184-196`
- JWT token management: `176-180`
#### Control Directory Watcher (`services/control-dir-watcher.ts`)
- Monitors external session changes: `20-175`
- HQ mode integration: `116-163`
- Detects new/removed sessions
#### Shutdown State (`services/shutdown-state.ts`)
- Global shutdown state tracking
- Allows components to check shutdown status
### Utilities
#### Logger (`utils/logger.ts`)
- Structured logging with file and console output: `1-186`
- Color-coded console with chalk
- Log levels: log, warn, error, debug
- File output to `~/.vibetunnel/log.txt`
- Debug mode via `VIBETUNNEL_DEBUG`
#### VAPID Manager (`utils/vapid-manager.ts`)
- Auto-generates VAPID keys for push notifications: `20-331`
- Stores in `~/.vibetunnel/vapid/keys.json`
- Key rotation support
#### Version (`version.ts`)
- Version info with build date and git commit
- Runtime version banner display
- Development mode detection
## Client Architecture (`src/client/`)
### Core Components
#### Entry Points
- `app-entry.ts:1-28`: Main entry, initializes Monaco and push notifications
- `test-entry.ts`: Test terminals entry
- `styles.css`: Global Tailwind styles
#### Main Application (`app.ts`)
- Lit-based SPA: `44-1401` - `<vibetunnel-app>`
- URL-based routing with `?session=<id>`
- Global keyboard handlers (Cmd+O, Escape)
- View management: auth/list/session
- Split-screen support for session list and detail
- **Events fired**:
- `toggle-nav`, `navigate-to-list`, `error`, `success`, `navigate`
### Component Event Architecture
```
vibetunnel-app
├── app-header (navigation, controls)
├── session-list (when view='list')
│ └── session-card (per session)
│ └── vibe-terminal-buffer (terminal preview)
├── session-view (when view='session')
│ ├── session-header
│ ├── vibe-terminal (main terminal)
│ ├── mobile-input-overlay
│ ├── ctrl-alpha-overlay
│ └── terminal-quick-keys
├── session-create-form (modal)
├── file-browser (modal)
├── unified-settings (modal)
└── auth-login (when view='auth')
```
### Terminal Components
#### Terminal (`terminal.ts`)
- Full xterm.js implementation: `23-150+`
- Virtual scrolling: `537-555`
- Touch/momentum support
- URL highlighting integration
- Custom width selection
- **Events**: `terminal-ready`, `terminal-input`, `terminal-resize`, `url-clicked`
#### VibeTunnelBuffer (`vibe-terminal-buffer.ts`)
- Read-only terminal preview: `26-268`
- WebSocket buffer subscription
- Auto-resizing
- **Events**: `content-changed`
#### SessionView (`session-view.ts`)
- Full-screen terminal view: `52-200+`
- Manager architecture:
- ConnectionManager: SSE streaming
- InputManager: Keyboard/mouse
- MobileInputManager: Mobile input
- DirectKeyboardManager: Direct keyboard access
- TerminalLifecycleManager: Terminal state
- **Events**: `navigate-to-list`, `error`, `warning`
### Session Management Components
#### SessionList (`session-list.ts`)
- Grid/list layout: `34-150+`
- Hide/show exited sessions
- Search and filtering
- **Events**: `navigate-to-session`, `refresh`, `error`, `success`
#### SessionCard (`session-card.ts`)
- Individual session display: `25-150+`
- Live terminal preview
- Activity detection
- **Events**: `session-select`, `session-killed`, `session-kill-error`
#### SessionCreateForm (`session-create-form.ts`)
- Modal dialog: `27-381`
- Command input with working directory
- Native terminal spawn option
- **Events**: `session-created`, `cancel`, `error`
### UI Components
#### AppHeader (`app-header.ts`)
- Main navigation: `15-280+`
- Session status display
- Theme toggle
- **Events**: `create-session`, `hide-exited-change`, `kill-all-sessions`, `logout`
#### FileBrowser (`file-browser.ts`)
- Filesystem navigation: `48-665`
- Browse and select modes
- Monaco editor preview
- **Events**: `file-selected`, `browser-cancel`
#### UnifiedSettings (`unified-settings.ts`)
- Settings modal with multiple tabs
- Terminal preferences, notifications, appearance
- **Events**: `close`, `notifications-enabled`, `success`, `error`
#### LogViewer (`log-viewer.ts`)
- Real-time log display: `1-432`
- SSE-style polling
- Level filtering and search
### Services
#### BufferSubscriptionService (`buffer-subscription-service.ts`)
- WebSocket client: `77-164`
- Binary protocol decoder: `234-259`
- Auto-reconnection with backoff
- Per-session subscriptions
#### WebSocketInputClient (`websocket-input-client.ts`)
- Alternative input method via WebSocket
- Low-latency keyboard/mouse input
- Fire-and-forget protocol
#### PushNotificationService (`push-notification-service.ts`)
- Service worker registration
- Push subscription management
- Notification action handling
#### AuthClient (`auth-client.ts`)
- Token management: `27-100+`
- SSH key and password authentication
- API header generation
- SSH agent integration
### Utils
#### Terminal Utils
- `terminal-renderer.ts:276-418`: Binary buffer decoder and HTML generation
- `terminal-utils.ts:14-44`: Terminal resize helpers
- `terminal-preferences.ts`: Width preferences management
- `xterm-colors.ts`: Color palette definitions
- `url-highlighter.ts`: URL detection and click handling
#### UI Utils
- `responsive-utils.ts`: Media query observer
- `title-updater.ts`: Dynamic document title
- `keyboard-shortcut-highlighter.ts`: Shortcut formatting
- `offline-notification-manager.ts`: Offline detection
#### General Utils
- `cast-converter.ts:31-82`: Asciinema v2 parser
- `logger.ts`: Namespaced console logging
- `path-utils.ts`: Path formatting and clipboard
- `constants.ts`: UI timing, breakpoints, z-indexes
## Forward Tool (`src/server/fwd.ts`)
### Purpose
CLI tool for spawning PTY sessions using VibeTunnel infrastructure.
### Usage
```bash
pnpm exec tsx src/fwd.ts [--session-id <id>] [--title-mode <mode>] <command> [args...]
# Examples
pnpm exec tsx src/fwd.ts claude --resume
pnpm exec tsx src/fwd.ts --session-id abc123 --title-mode dynamic bash -l
```
### Key Features
- Interactive terminal forwarding: `58-289`
- Automatic shell alias support via ProcessUtils
- Session ID pre-generation support
- Terminal title management modes
- Graceful cleanup on exit
### Title Modes
- `none`: No title management (default)
- `filter`: Block all title changes
- `static`: Show working directory and command
- `dynamic`: Show directory, command, and activity
### Integration Points
- Uses central PTY Manager: `147-150`
- Terminal resize synchronization: `257-269`
- Raw mode for proper input capture: `276-277`
- Auto-detects Claude commands for dynamic titles: `135-143`
## Build System
### Main Build (`scripts/build.js`)
- Asset copying: `7-121`
- CSS compilation with Tailwind
- Client bundling with esbuild
- Server TypeScript compilation
- Native executable creation
### Native Binary (`scripts/build-native.js`)
- Node.js SEA integration: `1-537`
- node-pty patching for compatibility: `82-218`
- Outputs:
- `native/vibetunnel`: Main executable
- `native/pty.node`: Terminal emulation
- `native/spawn-helper`: Process spawning (macOS)
- `native/authenticate_pam.node`: PAM auth
## Key Files Quick Reference
### Server Core
- `src/cli.ts`: Main entry point
- `src/server/server.ts`: Server implementation
- `src/server/middleware/auth.ts`: Authentication
- `src/server/routes/sessions.ts`: Session API
- `src/server/routes/websocket-input.ts`: WebSocket input
- `src/server/pty/pty-manager.ts`: PTY management
- `src/server/services/terminal-manager.ts`: Terminal state
- `src/server/services/activity-monitor.ts`: Activity tracking
- `src/server/fwd.ts`: CLI forwarding tool
### Client Core
- `src/client/app-entry.ts`: Entry point
- `src/client/app.ts`: Main SPA
- `src/client/components/terminal.ts`: Terminal renderer
- `src/client/components/session-view.ts`: Session viewer
- `src/client/services/buffer-subscription-service.ts`: WebSocket
- `src/client/services/websocket-input-client.ts`: Low-latency input
- `src/client/utils/cast-converter.ts`: Asciinema parser
### Configuration
- Environment: `PORT`, `VIBETUNNEL_USERNAME`, `VIBETUNNEL_PASSWORD`, `VIBETUNNEL_DEBUG`
- CLI: `--port`, `--username`, `--password`, `--hq`, `--hq-url`, `--name`
- Debug logging: Set `VIBETUNNEL_DEBUG=1` or `true`
### Protocols
- REST API: Session CRUD, terminal I/O
- SSE: Real-time asciinema streaming
- WebSocket: Binary buffer updates
- WebSocket Input: Low-latency keyboard/mouse
- Control pipes: External session control
### Session Data Storage
Each session has a directory in `~/.vibetunnel/control/[sessionId]/`:
- `session.json`: Session metadata
- `stream-out`: Asciinema cast file
- `stdin`: Input pipe
- `control`: Control pipe
- `stdout`: Raw output file
- `activity.json`: Activity status
## Development Notes
### Recent Improvements
- WebSocket input endpoint for lower latency
- Advanced terminal title management system
- Unified shutdown state management
- Atomic file writes for session data
- Split-screen support in web UI
- Enhanced activity monitoring with 100ms precision
- Improved command resolution for aliases
- Better error messages for spawn failures
### Testing
- Unit tests: `pnpm test`
- E2E tests: `pnpm run test:e2e`
- Vitest configuration with coverage
### Key Dependencies
- node-pty: Cross-platform PTY
- @xterm/headless: Terminal emulation
- Lit: Web components
- Express: HTTP server
- web-push: Push notifications
- TailwindCSS: Styling