Build a TermSurf host
Own panes, client sockets, engine routing, and compositing—the role Astrohacker TermSurf (ahterm) plays today.
What you are building
The host is the middle of the multi-process design. It owns panes and window layout, the client-facing Unix domain socket, routing between pane clients and engine helpers, and compositing of engine surfaces into those panes. It is not the Chromium process and not an app’s loopback UI server.
Shipped reference
Product host: Astrohacker TermSurf.app / ahterm (Ghostty-based). Protocol name remains TermSurf. This page is role-level: implement the contracts App and Engine clients already rely on.
Topology
client (ahweb / app)
│ connect(TERMSURF_SOCKET)
│ framed TermSurfMessage
▼
HOST (ahterm) ◄── listen; inject SOCKET + PANE_ID
│ spawn/attach engine --ipc-socket=…
│ framed TermSurfMessage
▼
engine (ah-chromiumd)Same frame format on both links: Wire.
Client session lifecycle
- Own a listen path for pane clients (Unix domain socket file).
- When starting a shell or app in a pane, inject:
TERMSURF_SOCKET— path of the host listen socketTERMSURF_PANE_ID— this pane’s id
- Accept connections; read and write length-prefixed frames.
- Handle client intents (names → Messages):
Representative host → client replies:
App-side view of the same contract: App how-to.
Engine session lifecycle
- Spawn or attach a helper (product:
ah-chromiumd) with--ipc-socket=<path>and profile / user-data / browser-name as needed. - Expect ServerRegister; then create tabs and wait for TabReady.
- Forward host→engine work (create/resize/close, navigate, input, presentation) and consume engine→host events (surfaces, URL/load, navigation state, dialogs, crashes).
Routing surface (representative):
Helper-side view: Engine how-to.
Pane ↔ tab binding
Clients speak pane. Engines speak tab. Only the host binds pane_id to tab_id. Do not make clients invent tab ids or make engines own window layout.
Compositing
When the engine reports CaContext / RenderSurface (and related presentation signals), place that content into the correct pane region. The host owns splits, focus chrome, and the product window. Platform-specific layer attachment is an implementation detail of ahterm—not a second protocol.
Framing
Duties checklist
- Own panes, window chrome, and split layout
- Listen for pane-client TermSurf connections on a host-owned Unix domain socket
- Inject TERMSURF_SOCKET and TERMSURF_PANE_ID into pane children
- Spawn or attach engine helpers with --ipc-socket (and product profile argv)
- Bind pane_id (clients) to tab_id (engine) and route framed messages
- Composite engine surfaces into pane regions
Do and don’t
- Do own layout, pane ids, and routing tables.
- Do inject
TERMSURF_SOCKETandTERMSURF_PANE_IDfor children. - Don’t embed a full browser engine in every client.
- Don’t invent a public TCP TermSurf API.
- Don’t let clients assign native tab ids.
Where to look
- Product clients that require host injection:
rust/ahweb/src/ipc.rs,bun/ahcalc/app/cli/termsurf-client.ts - Engine attach contract:
rust/ah-chromiumd/src/main.rs - Host integration lives in the Ghostty/ahterm TermSurf surfaces (local fork checkout when present)—role contracts above are the public implementer API.
- Wire · Messages · App · Engine
Next
With App, Engine, Host, Messages, and Wire documented, the protocol docs set covers the three implementer roles. Optional narrative: the whitepaper issue. Issue close is a separate manual gate after you accept the full goal.
Overview: TermSurf.
