The Harvest #9 - Multi-Interface Applications
Last month Ibegan exploringan agnosticRequest/Responsepattern so I could use the same router library for both a cli and a server.
Request
| type | server | cli |
|---|---|---|
Vec<String> | path | positional args |
HashMap<String,Vec<String>> | query params | options/flags |
This is howbeetstack.dev works currently, the cli that builds the server and static html uses the same router as the server itself. A single project often contains several applications and using the same primitives reduces complexity.
But what happens if you go the other way? A single application with multiple interfaces?
Prior Art - Content Negotiation
Ever heard ofcontent negotiation? I'm embarresed to say that I only discovered it last week. The original vision for the web was so cool! Servers that treat clients as individuals who maintain maximum control, deciding how they'd like their content delivered:
# browser wants html GET /users/42 User-Agent: Mozilla/5.0 Accept: text/html # api wants json GET /users/42 User-Agent: curl/8.0 Accept: application/json
All kinds ofAccept-headers exist, even for spoken languages!

Content negotiation fell out of favor for avariety of reasons, but the rise of clankers seems to have driven a bit of a resurgence. Wordpressrecently announcedsupport for theAccept: text/markdownheader in their api docs:
curl -sH 'Accept: text/markdown' https://developer.wordpress.org/reference/functions/get_permalink
Beyond HTML
The above example demonstrates serving either the raw data (json) or a html view (html/md), but what if we took this idea a little further?
Seeing as terminals are so hot right now, maybe you'd like to build a TUI alongside your website. Or maybe you'd like to provide the view as a json tree structure for a game engine ui. Usually that means writing two entirely seperate frontends, but it sounds like the kind of thing a framework should be able to do.
fn render_page(request: Request, page: Page) -> Response{ match request.header("User-Agent"){ "Mozilla/5.0" => into_html(page), "openclaw/2026.3" => into_markdown(page), "curl/8.0" => into_ansi(page), "bevy/0.18" => into_scene(page), } }
Beyond HTTP
Seeing as the Request/Response types are already agnostic to the transport we can start getting weird, for instance instead of sending the payload as a response body treating the request as an instruction to update an in-process persistent interface.
Take ThePrimeagean's very coolssh terminal.shopidea. Currently visitingterminal.shopin the browser simply tells you to go to your terminal, but with this technique a single server could be used to ssh into as a TUI, and also servethat exact same applicationas a web app. Heck while we're at it why not also spin up a native app usingbevy_ui!
Write once, deploy everywhere, but like this time for realsies.