# PtyKit Benchmark Results ## Environment - Bun: 1.3.14 - Node: 24.3.0 - OS: darwin arm64 - CPU: Apple M2 × 8 - RAM: 8.6 GB - Commit: 3a9837a - Generated by `bun bench.ts`. Numbers are machine-specific (a dev laptop, not a server). ## Point 5 — Throughput: raw bun-pty vs PtyKit-wrapped - Workload: `seq 1 1000000` to completion via an interactive shell, 6 runs (first dropped), median. - Raw bun-pty: 5.0 MB/s - PtyKit-wrapped: 4.7 MB/s - **Overhead: 5.3%** (target <10%) - Verdict: ✅ within target > The wrapped path additionally persists every chunk to a headless xterm (R7a) and > micro-task batches before fan-out — the legitimate cost being measured. ## Point 3 — Reattach latency & correctness | buffer target | serialize p50 | serialize p95 | frame size | latest line present | |---------------|---------------|---------------|------------|---------------------| | 10KB (1500 lines) | 3.128ms | 10.043ms | 7.9KB | ✅ | | 100KB (16000 lines) | 9.488ms | 18.828ms | 34.4KB | ✅ | | 1MB (170000 lines) | 9.507ms | 18.494ms | 39.3KB | ✅ | > Reattach replays one serialized frame (screen + 5000-line scrollback), not raw byte > history — frame size is bounded and the newest output is always present. Lines older > than the 5000-line window roll off by design (R6); that is not data loss within > the documented window. ## Point 4 — Idle memory footprint (bun-pty) | sessions | parent ΔRSS total | parent ΔRSS / session | |----------|-------------------|------------------------| | 50 | 0.0 MB | 0.00 MB | | 100 | 13.7 MB | 0.14 MB | > This is the **parent** process RSS delta (the headless xterm + bookkeeping per session). > Each PTY is a separate OS process whose memory is NOT counted here. The per-session > parent cost is dominated by the 5000-line headless xterm and is small; it is the gate > for any future disk-spill decision and did not warrant one at this scale. ## Point 7 — Backend auto-detect - Runtime detected: `bun-pty` (this run is under Bun). - bun-pty spawn under Bun: ✅ works - node-pty spawn under Node 25.9.0 (macOS arm64): ❌ **`posix_spawnp failed`** — reproduced with raw node-pty too, so it is a node-pty/Node-25 native issue, not a ptykit bug. (The library itself now loads cleanly under Node after fixing the `@xterm/headless` CJS named-import interop.) **Deploy-scenario matrix {direct, bundled, pm2/systemd, Docker} × {Bun, Node}:** NOT RUN — this machine is macOS with no Docker/pm2/systemd. Only the `direct` cells were exercised: Bun→bun-pty ✅, Node→node-pty ❌ (above). **Decision:** node-pty stays **experimental**. bun-pty is the default, tested path. The node-pty adapter is implemented and type-correct, but the gating data does not yet justify promoting it to "supported". ## Transport parity (SSE vs WebSocket) Not applicable — WebSocket is the only transport. There is no SSE path to compare against. ## End-to-end reattach via the client Covered as an automated test rather than a bench script: see `src/e2e.test.ts` (`real bun-pty: reattach replays scrollback and input keeps working`), which creates a session, runs a command, disconnects, reattaches with a fresh client, verifies the scrollback replay contains the earlier marker, and confirms input still works.