Passthrough Design
The Actor survives because it doesn’t accumulate. Passthrough design guarantees OOM immunity.The Design Choice
Copy
┌─────────────────────────────────────────────────────────────────┐
│ PASSTHROUGH vs ACCUMULATE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ACCUMULATE DESIGN (Problem): │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ PTY Output: 1GB │
│ ↓ │
│ Actor stores data internally │
│ ↓ │
│ Memory grows: 1GB+ │
│ ↓ │
│ OOM → Actor dies → All terminals dead │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ PASSTHROUGH DESIGN (MonoTerm): │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ PTY Output: 1GB │
│ ↓ │
│ SessionActor: Receives → Forwards immediately │
│ ↓ │
│ Memory stays: ~KB (only channel references) │
│ ↓ │
│ OOM IMPOSSIBLE │
│ │
└─────────────────────────────────────────────────────────────────┘
How Passthrough Works
Copy
┌─────────────────────────────────────────────────────────────────┐
│ SESSIONACTOR: PASSTHROUGH ARCHITECTURE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ SessionActor (Rust) │ │
│ │ │ │
│ │ What it DOES NOT store: │ │
│ │ • Raw PTY data │ │
│ │ • Parsed output │ │
│ │ • Terminal history │ │
│ │ │ │
│ │ What it DOES store: │ │
│ │ • MPSC channel senders (references only) │ │
│ │ • Session metadata (~bytes per session) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ DATA FLOW: │
│ │
│ PTY ──▶ Actor receives ──▶ Actor forwards ──▶ GridWorker │
│ │ │ │
│ │ └── IMMEDIATE (no storage) │
│ │ │
│ └── Actor memory: UNCHANGED │
│ │
└─────────────────────────────────────────────────────────────────┘
Memory Comparison
Copy
┌─────────────────────────────────────────────────────────────────┐
│ MEMORY FLOW: 1GB PTY OUTPUT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ +----------------+-----------------+-------------------------+ │
│ | Component | Accumulate | Passthrough | │
│ +----------------+-----------------+-------------------------+ │
│ | Actor | 1GB+ (grows) | ~KB (fixed) | │
│ | GridWorker | N/A | ~2-5MB (bounded) | │
│ | Total | UNBOUNDED | BOUNDED | │
│ | OOM Risk | HIGH | NONE | │
│ +----------------+-----------------+-------------------------+ │
│ │
│ WHY GRIDWORKER IS BOUNDED: │
│ │
│ • Alacritty VTE Parser with scrollback limit │
│ • Old lines auto-deleted (ring buffer) │
│ • Maximum ~10,000 lines retained │
│ • Memory caps at ~2-5MB regardless of output volume │
│ │
└─────────────────────────────────────────────────────────────────┘
Why the Actor Cannot Die
Copy
┌─────────────────────────────────────────────────────────────────┐
│ ACTOR DEATH CONDITIONS ANALYSIS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ +---------------------+--------------------------+------------+│
│ | Death Condition | Current Status | Possible? |│
│ +---------------------+--------------------------+------------+│
│ | OOM (Out of Memory) | Passthrough design | IMPOSSIBLE |│
│ | | prevents accumulation | |│
│ +---------------------+--------------------------+------------+│
│ | Panic (crash) | Almost no panic points | ~0.001% |│
│ | | in Actor code path | (annual) |│
│ +---------------------+--------------------------+------------+│
│ | Stack overflow | No recursion in Actor | IMPOSSIBLE |│
│ +---------------------+--------------------------+------------+│
│ | External kill | Actor runs in same | IMPOSSIBLE |│
│ | | process as Tauri app | (unless |│
│ | | | app exits) |│
│ +---------------------+--------------------------+------------+│
│ │
│ CONCLUSION: Actor death probability < 0.001% │
│ (less than once per year of continuous use) │
│ │
└─────────────────────────────────────────────────────────────────┘
OnceLock: The Consequence
Copy
┌─────────────────────────────────────────────────────────────────┐
│ PASSTHROUGH → SURVIVAL → ONCELOCK │
├─────────────────────────────────────────────────────────────────┤
│ │
│ LOGICAL CHAIN: │
│ ━━━━━━━━━━━━━━ │
│ │
│ 1. Passthrough Design │
│ → Actor doesn't store data │
│ → OOM impossible │
│ │
│ 2. No panic points in Actor path │
│ → Crash probability ~0.001% │
│ │
│ 3. Actor practically immortal │
│ → Channel sender never needs replacement │
│ │
│ 4. If channel never replaced... │
│ → Only need: 1 write (init) + infinite reads │
│ │
│ 5. std::sync::OnceLock │
│ → 1 write + infinite lock-free reads │
│ → PERFECT FIT │
│ │
└─────────────────────────────────────────────────────────────────┘
Performance Under High Load
Copy
┌─────────────────────────────────────────────────────────────────┐
│ HIGH OUTPUT SCENARIO: 10 TERMINALS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ SCENARIO: 10 terminals × 1000 messages/sec = 10,000 reads/sec │
│ │
│ WITH MUTEX: │
│ ━━━━━━━━━━━ │
│ │
│ Terminal 1: lock() → get → unlock → send │
│ Terminal 2: lock() WAIT... │
│ Terminal 3: lock() WAIT... │
│ Terminal 4: lock() WAIT... │
│ │
│ Problems: Sequential, contention, context switches │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ WITH ONCELOCK: │
│ ━━━━━━━━━━━━━━ │
│ │
│ Terminal 1: get() → send (instant) │
│ Terminal 2: get() → send (instant) │
│ Terminal 3: get() → send (instant) │
│ Terminal 4: get() → send (instant) │
│ │
│ Benefits: Fully parallel, no contention, no latency │
│ │
│ +----------------+-----------------+------------------+ │
│ | Metric | Mutex | OnceLock | │
│ +----------------+-----------------+------------------+ │
│ | Locks/sec | 10,000 | 0 | │
│ | Contention | Possible | None | │
│ | Context switch | Possible | None | │
│ | Latency | Accumulates | None | │
│ +----------------+-----------------+------------------+ │
│ │
└─────────────────────────────────────────────────────────────────┘
Complete Protection Chain
Copy
┌─────────────────────────────────────────────────────────────────┐
│ COMPLETE MEMORY PROTECTION CHAIN │
├─────────────────────────────────────────────────────────────────┤
│ │
│ PTY Daemon (Rust Sidecar) │
│ ↓ Unix Socket (blocking read) │
│ Socket Reader │
│ ↓ Unbounded MPSC Channel │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ SessionActor ← PASSTHROUGH DESIGN │ │
│ │ │ │
│ │ • Doesn't store data │ │
│ │ • Forwards immediately │ │
│ │ • Memory: ~KB (channel refs only) │ │
│ │ • OOM: IMPOSSIBLE │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ Unbounded MPSC Channel │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ GridWorker + Alacritty ← OVERWRITE DESIGN │ │
│ │ │ │
│ │ • Scrollback limit (10,000 lines) │ │
│ │ • Old lines auto-deleted │ │
│ │ • Memory: ~2-5MB (bounded) │ │
│ │ • OOM: IMPOSSIBLE │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ↓ ACK-gated (backpressure) │
│ Frontend (xterm.js + WebGL) │
│ │
│ ═══════════════════════════════════════════════════════════ │
│ │
│ TWO COMPLEMENTARY PROTECTIONS: │
│ │
│ 1. PASSTHROUGH (Actor) → Don't accumulate, forward immediately │
│ 2. OVERWRITE (State) → Don't queue, keep only current state │
│ │
│ Together they guarantee: │
│ • No unbounded memory growth anywhere in pipeline │
│ • Actor survival │
│ • System stability │
│ │
└─────────────────────────────────────────────────────────────────┘
Summary
Copy
┌─────────────────────────────────────────────────────────────────┐
│ KEY TAKEAWAYS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. PASSTHROUGH DESIGN │
│ SessionActor forwards data immediately, never stores │
│ → OOM impossible │
│ │
│ 2. ACTOR SURVIVAL │
│ No OOM + no panic points = Actor practically immortal │
│ → Channel replacement never needed │
│ │
│ 3. ONCELOCK CHOICE │
│ 1 write + infinite lock-free reads = perfect fit │
│ → No external dependencies, maximum simplicity │
│ │
│ 4. SMPC APPLIED │
│ "Simplicity is Managed Part Chaos" │
│ → Choose the simplest tool that works │
│ → Don't add features you won't use │
│ │
└─────────────────────────────────────────────────────────────────┘