intertwingly

It’s just data

Terminals That Outlive the Laptop


This is a work in progress. It runs on my desk, against one remote machine, and has for about a day. I am writing it up now rather than later because the interesting part is not the code — there is not much of it — but the realization that every piece needed already existed, and that the remaining work was to notice how they fit.

What I want

I want to open a terminal on any machine I have access to — the laptop in front of me, the Mac on the desk, a VPS, whatever a cloud provider rents by the hour — and then stop thinking about where it is. It should appear on my desktop as a window, tiled alongside every other window. Closing the lid, rebooting, walking to a different network: none of that should end what is running in it. When I come back, the window comes back, and what was running is still running.

Increasingly, what is running is a coding agent, and the thing I most want to know about a coding agent is whether it is waiting on me. A window that is busy can stay in the background. A window that is asking a question should say so before I go looking.

Who solved what

The surviving part was solved long ago. GNU Screen established the idea in 1987 that a terminal session belongs to a server process, not to the window you happen to be looking at it through; tmux refined it into the tool most of us actually use, and blazed the path everything below walks along: detach, reattach, sessions that outlive connections. Nothing in this post would make sense to someone who had not first internalized what tmux does.

The knowing part is newer. Herdr, by Can Celik, is a terminal runtime built around coding agents. Like tmux it keeps panes alive in a background server you attach to and detach from. Unlike tmux it watches what is on the screen and classifies it: this pane holds a Claude Code session, and that session is working, or idle, or blocked waiting on an approval, or done with work you have not yet looked at. It knows this for a couple of dozen agents, from screen evidence alone or from hooks the agents themselves can report through, and it publishes all of it — the panes, their state, and every change — over a small JSON socket API with a documented stability contract.

The place to put it together is Omarchy, DHH's opinionated Arch and Hyprland setup, whose fourth version replaced its status bar with a shell built on Quickshell and gave it a plugin system: a bar widget is a directory of QML, installed from any git URL, hot-reloaded on save, running inside the shell process with the same UI components the built-in widgets use. Omarchy already ships Herdr as a base package, with a keybinding to launch it and a set of shell helpers for laying out panes, so the two projects are well aware of each other.

And there were already people ahead of me. When I went looking for prior art I found five Omarchy plugins for Herdr, all from the last two months:

Each of them treats the Herdr window as the window: a multiplexer TUI you raise and then navigate inside. That is what Herdr is designed to be, and it is a good design. It was just not quite what I wanted.

The one primitive that changes the picture

Herdr can attach a raw terminal to a single pane, with none of its own chrome: herdr terminal attach <id>. Run that inside a terminal emulator and you get that pane's screen, and only that pane's screen, in that window. Close the window and you have detached; the pane carries on. Run it through ssh -t and the pane can be on any machine you can reach.

That is the whole trick. If every pane can be its own window, then Hyprland is the multiplexer — it already tiles, focuses, and moves windows better than any TUI will — and Herdr is reduced to the two things only it does: keep the panes alive, and know what the agents in them are doing. The status bar is where you find out; the window is where you go.

Reaching a remote Herdr from the desktop turned out to be equally mundane. Its API is newline-delimited JSON over a unix socket, and OpenSSH will forward a unix socket as readily as a port:

ssh -N -L /run/user/1000/agent-windows/mini.sock:/Users/rubys/.config/herdr/herdr.sock mini.local

To the plugin, that forwarded socket is indistinguishable from a local one. I subscribed to events through it, created and closed a tab on the Mac, and watched tab_created and tab_closed arrive on the laptop about 280 milliseconds later — most of which was the SSH round-trip of the command that caused them.

What exists now

rubys/omarchy-agent-windows is an Omarchy bar widget, about six hundred lines of QML and JavaScript plus three small bash scripts:

Nothing polls on a timer. Nothing upstream had to change: every call the plugin makes is in Herdr's released 0.9.0, and every UI piece is in Omarchy's public plugin contract. And it is an honest borrower: the socket lifecycle is adapted from carlotran4's model, the focus-workspace-then-window sequence and terminal launch from jankeesvw's script, and the repository says so file by file. Anyone is welcome to take anything back in the other direction.

What it is not, yet

It has been tested on exactly one topology: this laptop running Omarchy 4.0.3 and the foot terminal, talking to a MacBook running Herdr 0.9.0 in its default session. Named sessions, Linux remotes, other terminals: untested. The red border has been exercised with synthetic tags but not yet by an agent actually asking me something, though the event that would trigger it is the same one I watched arrive from the Mac.

The known sharp edge is one tmux users will recognize: if the Mac's own Herdr client is attached to the same pane, the two clients contend for the grid size. The fix is not to do that, or to let the desktop take over.

And there is a symmetry question I have not settled. Closing a pane on the server closes its window here, which I like. Closing the window here only detaches, which is the point of the whole exercise — but it means "close" does two different things depending on which side you do it from. That is probably right. I am not sure yet.

Where this goes

The next machine is a Mac mini rather than the MacBook, and after that something rented. The plugin does not care; it is a list of SSH targets. What I want to learn is whether the status bar is the right surface for "which of my agents needs me" once there are more than a handful of them, and whether panes-as-windows holds up when a Hyprland workspace has a dozen of them from three machines.

None of this required anyone to accept a patch. Herdr's maintainers run their project with agents they control and do not take unsolicited pull requests, which is their call to make; a working plugin built on the public API is a better way to show them a usage pattern than a proposal would be, and the only thing I would ask of them is that terminal attach and the socket contract stay as stable as their documentation already promises. Omarchy made it possible to ship a bar widget without asking anyone at all. And tmux, again, showed everybody that a terminal is something a server owns and a window merely visits.

Most of the code was written with Claude, in a Herdr pane on the laptop, while its counterpart on the Mac worked on something else in the window below — busy the whole time, as it happens, and never once red. I am still waiting for that.