State synchronization
evolvingState synchronization
TL;DR. How an attaching client catches up to a terminal that already has
history. The server synthesizes a snapshot of current engine state, the
client replays that snapshot into a fresh libghostty Terminal, and from
then on both ends stay in step by replaying the same PTY byte stream. A
separate on-disk journal for crash recovery is designed but not built; that
half is marked as such below.
Lazy state synchronization (built)
Both ends of the wire run libghostty, and the wire carries opaque terminal
bytes rather than a re-encoded grid
(ADR-0013). Live output is
therefore just forwarded: the server writes PTY bytes to its Terminal and
relays the same bytes, and each client writes them into its own Terminal.
Two engines fed identical bytes converge on identical state.
The interesting case is attach, when a client joins a terminal that already has scrollback and screen contents it never saw. phux handles this lazily, as specified by ADR-0018: rather than journaling every byte for the lifetime of the terminal, the server synthesizes a snapshot of the current engine state on demand and sends it once, at attach time.
The flow on attach:
- The server reads the current
RenderStateof the terminal’sTerminaland synthesizes a byte sequence that, when replayed into a fresh engine, reproduces that state. The synthesis algorithm is described inresearch/2026-05-25-libghostty-renderstate.md§7. - The client writes that snapshot into its fresh
libghostty_vt::Terminal. The client now holds a faithful mirror of the screen and scrollback as of the snapshot point. - Live PTY bytes that arrive after the snapshot are written into the same
Terminal, keeping it in step with the server from then on.
Because the snapshot is replayed through the same engine that produced it,
the client’s grid matches the server’s, up to the documented downsampling
rewrites. That equivalence is a property test, not an assumption; see
verification.md for how snapshot-on-attach replay is
checked.
This keeps synchronization cheap in the common case: a terminal nobody attaches to costs nothing beyond its own engine state, and an attach pays a single snapshot rather than a replay of the terminal’s entire history.
On-disk journal and crash recovery (designed, not built)
Status: design intent, not implemented as of 2026-06-06. Nothing in the server writes to disk today.
server.pid, per-terminal journals, and a--recoverflag do not exist. The notes below describe an intended shape, not current behavior.
The same bytes-on-wire shape that makes attach a snapshot-and-replay also makes crash recovery mechanical, because the PTY byte stream forwarded to clients is exactly what a journal would need to record. The designed shape:
- The server journals raw PTY output to disk, per terminal, in an append-only log. Logs are fsync’d on close and capped (a ring buffer, on the order of 10 MB per terminal in the current sketch).
- On startup, if a prior
server.pidis stale, the server can be invoked with--recover. It reads each journal, replays it into a freshlibghostty_vt::Terminal, and reconstitutes grouping from a metadata file stored alongside the journals.
In this design crash recovery falls out of the replay model rather than being a bolt-on: recovery is the attach snapshot path pointed at a journal instead of a live engine. It is recorded here as a direction; when it is built this section moves out of the designed-not-built block.