← the Atlas

Ghostex vs. kolu remote-terminals

analysis · budding ·

What Ghostex's PTY-persistence choice does and doesn't teach kolu's in-progress remote-terminals plan — one concrete borrow (honest pane⟂session state), one corroboration, one reframe; the "lean on a mux" swing doesn't survive review.

What Ghostex’s PTY-persistence choice does and doesn’t teach the in-progress kolu plan (remote-terminals + pty-daemon — read as docs/plans HTML at the time, since migrated to those Atlas notes). Sources read this turn: /tmp/Ghostex @ HEAD and both kolu plan docs. /lowy + /hickey folded in — my first three recommendations were largely wrong; this is the post-review version, and the headline below says what I retracted.

What Ghostex is (the relevant parts)

A native macOS app: web sidebar in a WKWebView, every terminal a real embedded Ghostty SurfaceView (Metal), not xterm.js. The native host owns PTY creation for live panes; node-pty is explicitly slated for removal once Ghostty owns all PTYs. (docs/native-ghostty-handover.md “Code To Remove”, shared/terminal-host-protocol.ts:1-6.)

Persistence is a pluggable provider, decoupled from the app process entirely:

Remote = persistence, same mechanism. Android/iOS/TUI connect to the Mac over SSH and attach to the very same named mux session that provides local survival; the phone asks the Mac-side ghostex CLI for session inventory + attach commands. First Android release is “ZMX only,” with a warm pool of recently-tapped sessions and an app-owned SSHJ transport. (docs/android-handover.md “Current State”; AllFeatures.md:77, :186.)

The architectural contrast

ConcernGhostexkolu (planned)
Who owns PTY lifetimeExternal mux (forked zmx / tmux / zellij). App is a pure attach/detach client.First-party @kolu/pty-host daemon the app spawns and supervises (own cgroup, survives systemctl restart).
Survive app/server restartFree — the app never owned the PTY, so a restart is never a daemon restart. No hazard.Engineered — pid-gate, cgroup-escape, reattach-by-id, recovery sequence. The #1034 hazard class lives here.
Metadata / repaint / replay IPCCustom — required forking zmx to add it. Now owns mux source + IPC + a user-session model.Custom — typed oRPC surface + build-time staleness hash + a provider DAG that runs fresh in kolu-server off the daemon’s stream. Owns only a headless PTY.
Local-survival vs remote-reachOne primitive: a named durable session. Remote = ssh mac → attach <name>. (Still two transports underneath: zmx + SSH.)One TerminalBackend interface, two implementations (loopback now, ssh in R-2) behind one stable unix socket. Same shape, different name.
Terminal rendererNative Ghostty/Metal. No web terminal.@xterm/headless mirror in the daemon + xterm in the client.
Honest liveness statenativePaneState (mounted/unmounted) ⟂ providerSessionState (exists/missing/persistence-disabled/unknown), derived isLive. (docs/terminology.md)Being built into B (status chip, degraded canvas) — the lesson #1034 paid for.

What survives review — the actual influence

Two reasoning errors I made (so the next pass doesn’t)


Net: the influence is one concrete borrow (Ghostex’s honest pane⟂session state vocabulary into kolu’s B-phase), one corroboration (reattach-by-id is a natural cut), and one reframe (#1034 is a lifecycle bug to pay down, not a seam to redraw). The tempting big swing — “lean on a mux instead of building the daemon” — does not survive a volatility/simplicity pass: Ghostex’s own zmx fork is the counter-evidence.