Skip to main content

Passthrough Design

The Actor survives because it doesn’t accumulate. Passthrough design guarantees OOM immunity.

The Design Choice

┌─────────────────────────────────────────────────────────────────┐
│  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

┌─────────────────────────────────────────────────────────────────┐
│  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

┌─────────────────────────────────────────────────────────────────┐
│  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

┌─────────────────────────────────────────────────────────────────┐
│  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

┌─────────────────────────────────────────────────────────────────┐
│  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

┌─────────────────────────────────────────────────────────────────┐
│  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

┌─────────────────────────────────────────────────────────────────┐
│  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

┌─────────────────────────────────────────────────────────────────┐
│  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                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘