Build a TermSurf app
A TermSurf app is a host client that puts a real GUI in a terminal pane—patterned on ahcalc and ahhelp.
What you are building
A TermSurf app is a client of the host. It does not embed Chromium and does not invent a second window system. It asks Astrohacker TermSurf to show UI in a pane—usually by pointing a browser overlay at a loopback URL your process serves.
The host owns panes and compositing. The engine helper owns real browser tabs. Your app owns product logic and (typically) a small local HTTP UI.
Shipped examples
ahcalc— scientific calculator; full-pane web UI; one product binary with client and server roles.ahhelp— product help cheatsheet; same client-in-pane pattern.
Process model
┌─ pane (inside ahterm) ─────────────────┐ │ CLI client ──TermSurf──► host │ │ │ engine │ │ └──app control UDS──► UI server │ │ loopback HTTP ◄── UI │ └────────────────────────────────────────┘
- CLI client — runs in the pane shell context; must see
TERMSURF_*; connects to the host; sends framedSetOverlay. - Local UI server — serves the React (or other) UI on loopback; not the TermSurf host; not a public internet server by default.
- Host + engine — outside your app package (see Host / Engine stubs for later how-tos).
Preconditions
Run the client inside Astrohacker TermSurf so the host injects protocol environment. Outside TermSurf, a correct client fails closed (ahcalc reports missing TERMSURF_SOCKET / TERMSURF_PANE_ID).
Discovery
| Variable | Role |
|---|---|
| TERMSURF_SOCKET | Host Unix domain socket path — connect here. |
| TERMSURF_PANE_ID | Pane id for this client — put it on SetOverlay and queries. |
Full transport tables and framing bytes: Wire.
Minimal TermSurf sequence
- Connect a Unix domain socket to
TERMSURF_SOCKET. - Start (or locate) your loopback UI server and form a URL (for example
http://127.0.0.1:…/). - Send a framed
SetOverlaywith:pane_id=TERMSURF_PANE_ID- grid geometry (col, row, width, height) for the pane
url= your loopback UI- optional
profile/browserhints
- On resize (e.g. terminal SIGWINCH), update geometry and re-send SetOverlay.
- Closing the client socket clears overlays owned by that connection (host lifecycle).
Tools such as ahweb often use Hello first; ahcalc-class apps may open with SetOverlay alone. Field definitions: Messages · SetOverlay.
Framing (short)
Every TermSurf write is: 4-byte little-endian length + protobuf TermSurfMessage bytes. Do not send bare protobuf. Details and env traces: Wire.
App-private control plane
ahcalc and ahhelp also use a separate control Unix socket (for example under /tmp/…) so a second CLI invoke can talk to an already-running UI server. That protocol is yours—line-oriented JSON or similar—not TermSurf. Keep it distinct from TERMSURF_SOCKET.
Checklist
- Run inside Astrohacker TermSurf so TERMSURF_SOCKET and TERMSURF_PANE_ID are set
- Connect a Unix domain socket to TERMSURF_SOCKET
- Serve UI on loopback (or equivalent) and build a local URL
- Send a framed SetOverlay with pane_id, grid geometry, and that URL
- Re-send SetOverlay on pane resize when geometry changes
- Use a separate app-private control channel for server lifecycle if needed
Do and don’t
- Do prefer one product binary with client/server roles when practical (ahcalc pattern).
- Do fail closed when TermSurf env is missing.
- Don’t invent a host-side port registry for your app.
- Don’t treat TermSurf as a public TCP API.
- Don’t embed a browser engine in the app to “skip” the host—the product engine path is the host helper (Chromium as shipped).
