How TermSurf Talks: The Protocol Behind Browser Panes in Astrohacker Terminal
July 19, 2026 · Ryan X. Charles
You open a URL in a terminal pane and a real browser engine paints the page beside your shell. No alt-tab. No second app fighting for focus. Chromium, WebKit, or Ladybird—same product window.
That is not one magical binary doing everything. It is several processes that have to agree on layout, tabs, input, and page state. The agreement has a name: TermSurf.
Astrohacker Terminal is the product you install. TermSurf is the
protocol those pieces use to talk. This post is the builder’s tour: what
happens when you run ahweb, who is on the wire, and why the messages look the
way they do. For the full message catalog, see
TermSurf protocol docs. This is the story, not a field dump
of termsurf.proto.
What happens when you run ahweb
Pick a boring URL and a shipped engine:
ahweb --browser chromium https://example.com
Roughly:
ahwebis a client. It does not embed Chromium. It finds the Terminal host and asks for browser work on a pane (the region you see).ahtermis the host. It owns the window, panes, and routing. It places a browser overlay on that pane and talks to an engine helper.ah-chromiumd(or webkit / ladybird) is the engine. It owns a tab—a real document instance—and a render surface the host can composite.- The engine reports readiness, geometry context, URL, title, and load state. The host draws the page into the pane. Chrome (URL bar, modes) stays in the product UI, not in a random floating browser window.
The rule of thumb:
- Clients speak pane.
- Engines speak tab.
- The host binds the two.
If you only remember one thing about TermSurf, remember that.
The cast
| Role | Shipped binary | Job |
|---|---|---|
| Host | ahterm |
Panes, compositing, TermSurf routing |
| Client | ahweb |
Open URLs / drive browser panes from a tool |
| Engine | ah-chromiumd, ah-webkitd, ah-ladybirdd |
Real browser process for a profile |
| Shell | ahsh |
Astrohacker Shell—the shell product, not the browser spine |
Engine selectors for ahweb are family names: chromium, webkit,
ladybird. Gecko / ah-geckod is not released yet; the protocol leaves
room for it. There is no meta ah dispatcher. Reserved for later:
ahwallet.
Install story today: one Homebrew cask, Apple silicon, Astrohacker Terminal—see Terminal docs and Web docs.
brew tap astrohackerlabs/astrohacker
brew trust astrohackerlabs/astrohacker
brew install --cask astrohacker
That lands Astrohacker Terminal.app in /Applications, with PATH tools
including ahterm, ahweb, ahsh, and the engine helpers.
Two pipes: sockets and protobuf
Unix domain sockets
Host and client do not open a public TCP port for this. They use a Unix domain socket: local IPC, a path on the machine, not “listen on the internet.”
The client finds the host through the environment (notably
TERMSURF_SOCKET). Same idea for other TermSurf-adjacent session variables
like pane id. Local, fast, private to your Mac.
Protocol Buffers
The wire language is protobuf, defined in termsurf.proto. Every frame is
a TermSurfMessage: an envelope whose payload is exactly one typed
variant—create a tab, move the mouse, report the URL, answer a query.
Why not free-form JSON per tool?
- One schema for Zig/Rust/C++ sides of the stack.
- Compact frames when input and page events get noisy.
- A shared vocabulary so every new client does not invent its own dialect.
You do not need to love protobuf to use Terminal. You need to know it is the contract between host, clients, and engines.
Messages as jobs (not tag numbers)
Think in intents, not field IDs.
Session. Client says hello; host replies with what it can do. Host signals when a browser connection is ready for that work. Pane mode can flip between browsing and not.
Put a browser on a pane. Client asks the host to place or update an overlay (geometry, URL, profile, engine hints). DevTools gets its own overlay path. Splits are “open another region,” still host-owned layout.
Tabs and surfaces. Host tells the engine to create, resize, or close a tab. Engine answers with ready state and surface / layer context so the host can composite the page into the pane (on macOS, the path you care about is real compositing—not a screenshot loop).
Input. Keys, mouse, scroll, focus: host → engine, so the page receives what you did in that pane.
Page events. Engine → host: URL, title, loading, cursor, link targets, whether back/forward/refresh make sense. That is how the chrome stays honest while the engine owns the document.
Navigation as a product. Back, forward, and refresh are first-class actions—not only “set this URL string.” State messages tell the UI what is available.
Queries and polish. List tabs, ask for last URL or DevTools info, JS dialogs, HTTP auth, console lines, renderer crash signals. Some of that is product chrome; some of it is automation harness. Same protocol family.
We chose this set because a multi-process, multi-engine browser in a terminal needs exactly these seams. Not “RPC for everything.” Not a single WebView stuffed into every CLI.
Who talks to whom
ahweb ──► ahterm ──► ah-chromiumd / ah-webkitd / ah-ladybirdd
▲ │ │
└────────────┴──── events / replies ──┘
| Direction | Why |
|---|---|
| Client → host | Ask for overlays, navigation, splits, queries without embedding an engine |
| Host → engine | Create/resize/close tabs; inject input; drive load and navigation |
| Engine → host | Register, report surface and page state, surface dialogs/crashes |
| Host → client | Hello/ready, mode, query answers—enough state to run chrome or automation |
Dialog and auth replies close the loop when something outside the sandboxed page must answer a prompt. The host stays in the middle so the engine never has to know your pane layout, and the client never has to own a browser binary.
Why multi-process
Real engines are large, crashable, and security-sensitive. Isolating them is not optional if you want Chromium-class behavior in a product window.
Multi-process TermSurf also means:
- Many clients, one host.
ahwebtoday; other tools and apps later—same language. - No Chromium-in-every-CLI. The install stays one Terminal plus helpers, not a browser fork per command.
- Engine swap without rewriting clients. Pick
chromium,webkit, orladybird; the client still speaks pane-level intent.
In-process WebViews are simpler demos. They are a different product. We wanted real engines in panes. That forced a protocol.
What to do next
Install on Apple silicon with Homebrew:
brew tap astrohackerlabs/astrohacker brew trust astrohackerlabs/astrohacker brew install --cask astrohackerOpen Astrohacker Terminal, open a shell pane, and run
ahweb https://example.com(add--browser webkitorladybirdwhen you want to feel the multi-engine claim).Read TermSurf protocol if you implement a client or care about the full message list.
Read Web for the product tool surface and Terminal for the host.
The point of TermSurf is boring on purpose: one language so a real browser
and a real terminal can share a window without inventing a new wire format
every time we add a tool. When ahweb works, that language is doing its job.