VibeTunnel Architecture Analysis - Mario’s Technical Deep Dive
This document contains comprehensive technical insights from Mario’s debugging session about VibeTunnel’s architecture, critical performance issues, and detailed solutions.Executive Summary
Mario identified two critical issues causing performance problems in VibeTunnel:- 850MB Session Bug: External terminal sessions (via
fwd.ts
) bypass the clear sequence truncation instream-watcher.ts
, sending entire gigabyte files instead of the last 2MB - Resize Loop: Claude terminal app issues full clear sequence (
\x1b[2J
) and re-renders entire scroll buffer on every resize event, creating exponential data growth
System Architecture
Core Components
Detailed Sequence Flow
Key Files and Their Roles
File | Purpose | Critical Functions |
---|---|---|
server.ts | Main web server | HTTP endpoints, WebSocket handling |
pty-manager.ts | PTY lifecycle management | createSession() , setupPtyHandlers() |
stream-watcher.ts | Monitors ascinema files | sendExistingContent() - implements clear truncation |
fwd.ts | External terminal forwarding | Process spawning, BYPASSES TRUNCATION |
terminal-manager.ts | Binary buffer rendering | Converts ANSI to binary cells format |
Data Flow Paths
Input Path (Keystroke → Terminal)
- Browser captures key press
- WebSocket sends to
/api/sessions/:id/input
- Server writes to IPC socket
- PTY Manager writes to process stdin
- Process executes command
Output Path (Terminal → Browser)
- Process writes to stdout
- PTY Manager captures via
onData
handler - Writes to ascinema file (with write queue for backpressure)
- Stream watcher monitors file changes
- For existing content: scans for last clear sequence
- Client receives via:
- SSE:
/api/sessions/:id/stream
(text/ascinema format) - WebSocket:
/buffers
(binary cell format)
- SSE:
Binary Cell Buffer Format
The terminal manager pre-renders terminal output into a binary format for efficient transmission:- Server-side ANSI parsing eliminates client CPU usage
- Efficient binary transmission reduces bandwidth
- Only last 10,000 lines kept in memory
- Client simply renders pre-computed cells
Critical Bugs Analysis
1. The 850MB Session Loading Bug
Symptom: Sessions with large output (850MB+) cause infinite loading and browser unresponsiveness. Root Cause: External terminal sessions viafwd.ts
bypass the clear sequence truncation logic.
Technical Details:
- Test file: 980MB containing 2,400 clear sequences
- Server-created sessions: Correctly send only last 2MB
- External terminal sessions: Send entire 980MB file
- Processing time: 2-3 seconds to scan 1GB file
- Client receives instant replay for 2MB truncated content
sendExistingContent()
, sending gigabyte files to clients.
2. Resize Event Performance Catastrophe
Problem: Each resize event causes Claude to re-render the entire terminal history. Claude’s Behavior:- In 850MB session: each resize → full buffer re-render
- Claude renders from “Welcome to Claude” message every time
- Mobile UI particularly problematic (frequent resize events)
- Header button position shifts during rendering indicate viewport instability
- Session with 39 resize events can generate 850MB+ files
- React Ink (TUI framework) unnecessarily re-renders entire components
- Session-detail-view has buggy resize observer
- Mobile Safari behaves differently than desktop at same viewport size
- Touch events vs mouse events complicate scrolling behavior
3. Node-PTY Architecture Flaw (ALREADY FIXED)
This issue has been resolved by implementing a custom PTY solution without the shared pipe architecture.Ascinema Format Details
VibeTunnel uses the ascinema format for recording terminal sessions:Proposed Solutions
Priority 1: Fix External Terminal Clear Truncation (IMMEDIATE)
Problem: External terminal sessions don’t usesendExistingContent()
truncation.
Investigation Needed:
- Trace how
fwd.ts
connects to client streams - Determine why it bypasses stream-watcher’s truncation
- Ensure external terminals use same code path as server sessions
- Test with 980MB file to verify fix
Priority 2: Fix Resize Handling (INVESTIGATION)
Debugging Approach:- Instrument session-detail-view with resize observer logging
- Identify what causes viewport expansion
- Implement resize event debouncing
- Fix mobile-specific issues:
- Keyboard state affects scrolling
- Touch vs mouse event handling
- Scrollbar visibility problems
Implementation Details
Write Queue Implementation
The PTY manager implements backpressure handling:Platform-Specific Considerations
macOS:- Screen Recording permission required for terminal access
- Terminal.app specific behaviors and quirks
- Different behavior than desktop Safari at same viewport
- Touch events complicate scrolling
- Keyboard state affects scroll behavior
- Missing/hidden scrollbars
- Viewport meta tag issues
- ConPTY vs WinPTY support
- Different ANSI sequence handling
- Path normalization requirements
Performance Metrics
Metric | Current | After Fix |
---|---|---|
980MB session initial load | Infinite/Crash | 2-3 seconds |
Data sent to client | 980MB | 2MB |
Memory per terminal | 50-100MB | Target: 10MB |
Clear sequence scan time | N/A | ~2 seconds for 1GB |
Resize event storms | Exponential growth | Debounced |
Testing and Debugging
Test Large Session Handling
Debug Resize Events
Monitor Network Traffic
- Check
/api/sessions/:id/stream
response size - Verify only sends data after last clear
- Monitor WebSocket
/buffers
for binary updates
Architectural Insights
Why Current Architecture Works (When Not Bugged)
- Simplicity: “Es ist die todeleinfachste Variante” - It’s the simplest possible approach
- Efficiency: 2MB instead of 980MB transmission after clear sequence truncation
- Server-side rendering: Binary cell format eliminates client ANSI parsing
Community Contribution Challenges
- High development velocity makes contribution difficult
- “Velocity kills” - rapid changes discourage contributors
- LitElement/Web Components unfamiliar to most developers
- Large file sizes cause AI tools to refuse processing
Future Architecture Considerations
Go Migration Benefits:- Automatic test dependency tracking
- Only runs tests that changed
- Pre-allocated buffers minimize GC
- Better suited for AI-assisted development
- 2MB static binary
- 10MB RAM usage
- Direct C interop for PTY code
- No garbage collection overhead
Action Plan Summary
-
Immediate (End of Week): Fix external terminal truncation bug
- Debug why
fwd.ts
bypassessendExistingContent()
- Deploy fix for immediate user relief
- Debug why
-
Short Term: Comprehensive resize fix
- Debug session-detail-view triggers
- Implement proper debouncing
- Fix mobile-specific issues
-
Long Term: Consider architecture migration
- Evaluate Rust forward binary
- Consider Go for web server
- Maintain backwards compatibility
Key Technical Quotes
- “Wir schicken 2MB statt 980MB” - We send 2MB instead of 980MB
- “Die haben einen Shared Pipe, wo alle reinschreiben” - They have a shared pipe where everyone writes
- “Es gibt keinen Grund, warum ich von da weg alles neu rendern muss” - There’s no reason to re-render everything from the beginning
- “Das ist known good” - Referring to battle-tested implementations