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:

  1. ahweb is a client. It does not embed Chromium. It finds the Terminal host and asks for browser work on a pane (the region you see).
  2. ahterm is the host. It owns the window, panes, and routing. It places a browser overlay on that pane and talks to an engine helper.
  3. ah-chromiumd (or webkit / ladybird) is the engine. It owns a tab—a real document instance—and a render surface the host can composite.
  4. 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. ahweb today; 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, or ladybird; 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

  1. Install on Apple silicon with Homebrew:

    brew tap astrohackerlabs/astrohacker
    brew trust astrohackerlabs/astrohacker
    brew install --cask astrohacker
    
  2. Open Astrohacker Terminal, open a shell pane, and run ahweb https://example.com (add --browser webkit or ladybird when you want to feel the multi-engine claim).

  3. Read TermSurf protocol if you implement a client or care about the full message list.

  4. 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.

← Back to blog