1# Terminal runtime
2
3The environment server owns PTYs, session lifetime, and retained output. Every
4client, including the desktop renderer, attaches through the environment connection.
5This lets clients reconnect or share a running session. Renderer choices stay local
6to each client and do not change terminal contracts.
7
8Terminal I/O stays independent of chat traffic so stalled streams and queued
9compression cannot block keystrokes. The native desktop's own backend uses a
10MessagePort and inherited pipes; other environments use a lazy second WebSocket.
11Both paths share terminal handlers, ticket authorization, and session supervision.
12Metadata stays on the primary connection so an idle sidebar opens neither path.
13WSL uses WebSockets because its launcher does not preserve inherited descriptors.
14
15## Output and retention
16
17[Terminal history](../../apps/server/src/terminal/Manager.ts) is incremental.
18PTY callbacks append new chunks; live events carry only those chunks. Materializing
19or copying full scrollback on every callback makes output cost grow with retained
20history, so snapshots and coalesced persistence are the materialization boundaries.
21Persistence queues the mutable history buffer and reads its latest value when the
22write runs. Clear, restart, and close must drain writes before completing their
23lifecycle boundary.
24
25Server history is capped at 5,000 lines and 8 MiB of UTF-8 text per terminal, so a
26long unterminated line cannot bypass retention. Eviction removes the oldest output
27without splitting Unicode code points; live output is not truncated. Release
28discarded chunk references immediately, even if array compaction happens later.
29Client buffers have a separate 512 KiB cap. Measure throughput with full scrollback
30when changing this path.
31
32Restoration must read only the bounded tail of current or legacy history files,
33skip any incomplete UTF-8 prefix, and apply the line limit. Close the read handle
34before rewriting the capped file. Reading whole old logs would defeat the memory
35bound during startup.
36
37## Renderer ownership
38
39Android keeps its native libghostty-vt adapter. Web uses the vendored
40`@clo/terminal` package, whose Ghostty ABI pin and engine artifact live together in
41[`packages/terminal/vendor`](../../packages/terminal/vendor). Its DOM renderer owns
42frames and native selection; the T3 adapter owns environment transport and local
43interaction policy. One WebAssembly instance serves each browser tab, with separate
44terminal handles freed on disposal. The Android pin remains independent at
45[`native/libghostty-vt/VERSION`](../../native/libghostty-vt/VERSION).
46
47Restoring scrollback must not send terminal replies to the current shell. Historical
48device queries can otherwise provoke fresh replies that appear as junk at the
49prompt. The server strips query/response traffic from retained history, and the
50[web adapter](../../apps/web/src/terminal/TerminalSurface.ts) suppresses its PTY data
51sink during replay. Preserve both protections when changing retention or renderer code.