hive
A User’s Manual
A fleet console for coding agents.
Your laptop sleeps. The agents keep working.
Last revised: 2026-09-16
torsor lab
1 Preface: The Laptop Is the Wrong Place
The agent is still running and you need to close the lid.
Maybe you’re leaving the office. Maybe the battery is at 4%. Maybe
you just want to go to dinner. The agent doesn’t care — it’s forty
minutes into a refactor, and the moment your laptop sleeps, so does the
work. You’ve probably already tried the obvious fix: run it on a machine
that stays on, reach it over SSH, keep it alive inside
tmux. That works. It works right up until there are four
such machines, six such sessions, and no memory of which box you left
the interesting one on.
At that point you’re not running agents. You’re doing archaeology.
You SSH somewhere, run tmux ls, squint at names you chose
in a hurry, attach to the wrong one, detach, try the next box. And
because that ritual is tedious, you stop starting long jobs — which is
the real cost, and the one that’s hard to notice.
hive is a console for agent sessions that live on other machines.
You keep the agents where they belong — on boxes that stay powered on — and hive gives you one view of all of them: what’s running where, what each one last said, and a way to answer it. From a terminal. From a native app on your Mac. From your phone while you’re standing in line for coffee. Same fleet, same sessions, three different places to reach them.
The part that takes a moment to accept is that hive is not a single program you install. It is several programs and a handful of configuration files, spread across the machines you already have, and the arrangement is the product. One program runs on each box and owns the truth about what’s running there. One program runs on a single always-on machine and collects that truth from all the boxes. The things you actually touch — the terminal command, the Mac panel, the phone console — hold no state at all; they ask the collector. The configuration files say which machine is which.
That shape is why your laptop is allowed to sleep, and it’s why hive asks more of you up front than a one-line install would. You need machines that stay on, and you need them able to find each other. The Fleet lays out exactly what those machines are and what each one has to provide. Read it before you install anything; it’s short, and it’s the chapter that makes the rest make sense.
Quick Start is the fast path, and an unusual one: hand the repository to a coding agent and let it do the setup. This works better than it sounds, because hive’s own conventions are written down where an agent will find them — but only up to a point, and the chapter is mostly about where that point is. What an agent can do unattended, what it will have to ask you for, and how you tell a real success from a plausible-looking failure. Standing Up the Fleet covers the same ground by hand, slowly, for when you’d rather drive.
Then the payoff. Three Consoles is the hive
command, the Mac panel, the phone console — what each is good at, and
why chat from a phone turns out to be the feature that changes how you
work. Running Agents is starting agents and managing their
lifecycles. Chat and Transcripts is talking to a running agent
and reading what it said, including the one genuinely confusing thing in
the system, which is how a session gets attached to a transcript and
what to do when that attachment goes stale. Keeping It Running
is keeping a fleet healthy over months.
The appendix is the exhaustive part — every command, flag, endpoint, config field, and path. Nothing in the chapters tries to be complete. The appendix does.
2 The Fleet
Most software asks what operating system you have. hive asks something harder: which of your machines never turns off.
That’s the requirement underneath everything else in this manual. hive exists to keep agent sessions alive while you walk away, and nothing in the software can supply a computer that stays powered on. So before any install command, it’s worth being concrete about which machines you’re going to use and what job each one does. There are four jobs. One machine can hold more than one of them.
2.1 Four roles
An agent box is a machine that stays on and actually
runs the agents. Each one runs a small daemon called
hive-host, which starts agents inside tmux,
keeps a file describing each session, and answers questions about them
over HTTP. The box is the truth: if hive-host says a
session is running, it’s because the tmux session is really
there, on that machine, right now. Most fleets have two to four boxes.
One is enough.
The hub is one designated always-on machine that collects. Every few seconds it asks each box what it’s running and caches the answer. It also forwards commands — your “stop this session” goes to the hub, and the hub passes it to the right box. The hub is an ordinary agent box that has been given this extra job, so it can run agents too. What makes it the hub is that everything else depends on it being reachable, which means you should pick the machine you trust most to stay up.
A desktop client is your laptop — the
hive command in a terminal, or the Mac panel, or both.
Clients keep no state. They ask the hub and render what comes back,
which is precisely why the laptop is allowed to sleep, get closed, or
fly across an ocean.
A phone is the fourth. It loads a small web console served from the hub and talks to the same API your laptop uses. You install nothing on the phone beyond Tailscale itself.
2.2 What talks to what
laptop phone
hive CLI, Mac panel web console
| |
| HTTP + SSE |
+---------+---------+
|
v
hive-hub :8787
nginx :80 /hive/
|
| HTTP over the tailnet
+---------+---------+
| | |
v v v
box-a box-b hub
hive-host hive-host hive-host
:8788 :8788 :8788
| | |
tmux tmux tmux
Two things in that picture are worth saying out loud.
First, the clients never connect to a box. They talk to the hub, and only to the hub. This is what makes the phone console possible at all — one address to reach, one thing to be reachable.
Second, there’s no SSH on that diagram. Status, chat, starting an agent, stopping one: all HTTP. SSH appears in exactly two places in this manual — installing the software in the first place, and attaching a real terminal to a running agent when you want to type at it directly.
Note
The hub is listed twice above, as hub:8787 and
hub:8788. That isn’t a typo. A hub that also runs agents is
running two daemons: the collector on 8787 and its own
hive-host on 8788. The collector polls its own box over
HTTP just like any other.
2.3 What each machine has to provide
2.3.1 Every agent box, including the hub
A Linux machine with apt — the provisioning assumes the
Debian/Ubuntu family. It has to stay powered on and awake; a box that
suspends is a box whose agents stop.
You need four things on it before hive can be installed, and the installer cannot supply any of them for you:
Tailscale, up and authenticated. Every hive daemon binds to the machine’s Tailscale address and refuses to bind anywhere else.
SSH access from your laptop, under the account that will own the agents.
sudofor that account, for the first install only.The agent itself —
claude, orcodex, or both — installed and already signed in for that same account.
That last one is the requirement people miss. hive does not install
coding agents and does not hold credentials for them. It runs whatever
claude or codex it finds on the box, as you,
using the login you already did there. A box with no agent installed
will provision perfectly and then fail the moment you ask it to start a
session.
Everything else — git, tmux, a compiler, a
Rust toolchain, the hive binaries themselves — is installed for you
during setup.
Note
hive builds from source on each box. The first install on a new machine spends several minutes compiling Rust, which is normal and which you should not interrupt. Copying a binary from your Mac does not work; it’s the wrong architecture and the wrong operating system.
2.3.2 The hub, additionally
Whatever a box needs, plus a web server for the phone console, which the installer configures. Nothing more is asked of the machine. What’s asked of you is the choice: this is the machine that everything else routes through, and if it’s off, every client goes blind at once. The agents keep running. You just can’t see them.
2.3.3 Your laptop
For the terminal, one program: the hive command, plus a
two-line config file naming your hub.
For setting up the fleet, additionally ansible-core and
SSH keys to the boxes. Only the machine that provisions needs this — a
second laptop can use hive perfectly well with nothing but the
hive command and the hub’s address.
For the Mac panel, a macOS machine and a Node toolchain to build it. The panel is a native application that speaks to the hub over HTTP; it is not a browser window pointed at the hub.
2.3.4 Your phone
Tailscale, signed in to the same network. That’s the whole list. The console is a web page served by the hub, so there’s nothing to install and nothing to keep updated.
2.4 The tailnet is the authentication
hive has no accounts, no passwords, no tokens, and no TLS. Being on your Tailscale network is the credential.
This is a deliberate trade, and it’s the right one for a personal fleet — it’s what makes a phone console a fifteen-minute feature instead of an authentication project. But it has a consequence you should hold clearly in mind.
Warning
Anyone who can reach your tailnet can read your agents’ conversations, send them messages, start new agents on your machines, and stop running ones. There is no second check. Treat tailnet membership exactly as seriously as you would treat an SSH key to the same machines, and don’t add devices you don’t control.
The software enforces its half of the bargain strictly. Both daemons
refuse to start unless their bind address is a Tailscale address — they
will not bind to 0.0.0.0, and they will not bind to
loopback unless you explicitly pass a development flag. You cannot
accidentally publish your fleet to the open internet, because the
program will stop rather than let you.
2.5 What a session actually is
One agent, running on one box, under a name you chose.
The name is the handle for everything — a box and a task name identify a session completely:
hive stop box-a period-index
On the box, that name becomes two things: a tmux session
named hive- followed by the task, and a small metadata file
recording the working directory, which agent it is, and when it started.
Nothing else about a session lives anywhere — there’s no central
database, and the hub’s copy is just a cache it refreshes every few
seconds.
The practical consequence: the box is always right, and everything else is a view. If the hub says something the box disagrees with, the box wins, and the disagreement resolves itself within seconds.
2.6 What happens when a piece goes missing
Your laptop sleeps. Nothing happens. This is the whole point. Clients hold no state and nothing depends on them; agents keep working and you pick up where you left off.
A box goes down. Its row in the fleet table reads
(unreachable) with the error the hub got. Every other box
is unaffected — one dead machine doesn’t take down your view of the
rest.
The hub goes down. Every client goes blind at once:
no status, no chat, no starting or stopping anything. The agents
themselves are entirely unaffected, still running in tmux
on their boxes. When the hub comes back, so does your view, with no
reconciliation needed. If you need to reach an agent while the hub is
down, SSH to its box and attach to tmux directly — that
path never stops working.
Warning
hive is a console, not a supervisor. It shows you what your agents are doing and lets you talk to them. It does not restart an agent that crashed, resume one after a reboot, or notice that a session has been stuck waiting for input since Tuesday. Those remain your job.
That’s the whole architecture. Two daemons, some configuration files saying which machine is which, and three consoles that hold nothing. The next chapter puts it on real machines.
3 Quick Start: Let an Agent Do It
There’s a pleasing circularity to setting up hive by handing the repository to a coding agent and asking it to install the thing that will manage coding agents. It also happens to be the fastest route, because hive’s conventions are written down inside the repository in the places an agent looks first.
It is not, however, a route you can start and walk away from. This chapter is mostly about where the agent’s usefulness stops — what it can do unattended, what it has to ask you for, and how to tell a real success from a confident-sounding failure.
3.1 Before you start
Have this ready. None of it can be produced by the agent, and each missing piece stops the install at a different point.
The names of your boxes, as you SSH to them, and which one is the hub.
Tailscale running and signed in on every box, on your laptop, and on your phone.
Working SSH from your laptop to each box, and the
sudopassword for the account you SSH as.claudeorcodexinstalled and signed in on each box, under that same account.A URL the boxes can clone hive from. Each box fetches the source itself and builds it, so a repository that only exists on your laptop is not enough.
Note
The last two are the ones that quietly derail an otherwise clean install. Both boil down to the same question — can the box, acting as you, get what it needs without you sitting there? An agent signed in only on your laptop and a repository reachable only from your laptop will both pass every check until the moment they don’t.
3.2 What to ask for
Open an agent session in your clone of the hive repository and describe where you’re starting from. Concretely, and with your real machine names:
Read README.md, CLAUDE.md, and ansible/README.md.
I want to set up a hive fleet. My boxes are box-a and box-b,
and hub is the always-on machine that should run hive-hub.
All three are on my tailnet and I can SSH to them.
Walk me through it. Stop and ask before anything that needs
my password or changes a machine.
The last line matters more than the rest. Provisioning is the kind of task an agent will happily drive to completion, and you want the first converge to be something you watched.
3.3 What it can do for you
A capable agent working in that repository will get the shape right, because the repository tells it the shape. Expect it to handle:
The configuration files, correctly placed. hive
keeps everything about your fleet outside the repository, under
~/.hive/fleet/. There are two files, both copied from
examples in the repo and then edited: an inventory naming your boxes and
marking which one is the hub, and a vars file saying where the boxes
clone from. Getting these right by hand means reading a README; an agent
just does it.
Explaining what the converge will do before you run it, in the order it will happen: install packages, install Rust, clone hive on each box, build it there, install the daemons as services, then generate your laptop’s client files.
Reading the failures. This is where an agent genuinely earns its place. Ansible failures are long and the real error is rarely in the last line. An agent reads the whole thing and tells you which host failed at which task.
The known traps. Two are documented in the
repository, and an agent that read the docs will raise them before they
bite. On recent Ubuntu, sudo may be routed through an
implementation that Ansible’s password prompt cannot drive, and the fix
is a one-line switch run once per box before the first
converge. Separately, the daemons run as user services, which by default
stop when you log out — a setting called linger has to be
enabled once per box or your fleet goes quiet whenever your SSH session
ends.
3.4 What it will have to ask you for
Your sudo password. The first converge
installs system packages and creates a directory owned by root. Ansible
prompts for this, interactively, and an agent cannot type it for you —
nor should you want it to.
Which machine is the hub. There’s no way to infer it. It’s a judgment about which of your machines is most reliably on.
Authenticating Tailscale and the coding agents. Both are browser-based logins on each box. The install will complete without them and then fail in ways that look unrelated: a box that can’t be reached, or a session that starts and immediately dies.
Permission to push. The boxes clone from a URL, so whatever you want deployed has to be pushed there first. If the agent has been editing hive itself, the code on the boxes is whatever was pushed — not what’s in your working directory.
3.5 How to tell it actually worked
An install can report success at every step and still leave you with a fleet that doesn’t work, so check it yourself. Four checks, in order, each one testing a different link in the chain.
One. On each box, is the daemon running?
ssh box-a 'systemctl --user is-active hive-host'
You want active. Anything else and that box isn’t in the
fleet yet.
Two. Is it actually answering on the network, rather than merely running?
ssh box-a 'curl -s http://$(tailscale ip -4):8788/v1/sessions'
You want [] — an empty list of sessions. An empty
response instead means the daemon is up but not listening where you
think it is.
Three. Can the hub see everyone? From your laptop:
hive status
Every box should be listed, each saying (no sessions). A
box showing (unreachable) is the interesting case, and it
means the hub couldn’t reach it — which is a different problem from that
box being down, and Keeping It Running separates them.
Four. Start something real and watch it appear:
hive run box-a hello /home/you/some/project
hive status
The session should show as running, with the last line
of the agent’s output beside it. This is the check that exercises
everything at once — the client, the hub, the box, tmux,
and the coding agent’s own login. If the first three passed and this one
fails, the problem is almost always that last item.
Warning
Don’t accept “the playbook completed successfully” as evidence. Ansible reports on the tasks it ran, not on whether you have a working fleet — and the two most common failures, an unauthenticated Tailscale and an unauthenticated coding agent, both leave a converge looking perfectly clean. Run the four checks.
3.6 Then stop and read about the consoles
Once hive status lists your boxes, you’re done with
setup and you haven’t yet used hive for anything. The terminal is the
least interesting of its three faces; Three Consoles covers the
Mac panel and the phone console, which are what make a fleet feel like
something you can keep an eye on rather than something you have to
remember to check.
If the install didn’t go cleanly, or you’d rather understand the machinery before trusting it, Standing Up the Fleet walks the same path by hand.
4 Standing Up the Fleet
The same install as Quick Start, done deliberately. Read this if the agent route stalled, if you’d rather understand what’s happening to your machines, or if you’re adding a box to a fleet that already exists.
Provisioning uses Ansible, and it runs from your laptop. Your laptop is the only machine that needs it — the boxes need nothing but SSH and a willingness to compile.
4.1 What a converge does
In order, on every box:
Install system packages:
git,tmux, a compiler, the libraries hive needs.Install a Rust toolchain, if the box doesn’t have one.
Clone hive at a pinned version into
/srv/lab/hive.Build it there, from source.
Install
hive-hostas a service and start it.On the hub only: the same for
hive-hub, plus the phone console.Check that
tmuxand a coding agent are present, and say so if they aren’t.
Then, on your laptop, it writes the two client files that tell
hive where your fleet is.
Two things to notice. The build happens on each box — the first
converge on a new machine spends several minutes in cargo,
which is expected. And the last step only checks for a coding
agent; installing and signing in to claude or
codex on each box is yours to do, and the converge will
finish happily without it.
4.2 One: Ansible on your laptop
uv tool install ansible-core
4.3 Two: describe your fleet
Nothing about your machines lives in the hive repository — it’s
public, and it stays that way. Your fleet is described by two files
under ~/.hive/fleet/, copied from examples in the repo and
then edited. From your clone:
mkdir -p ~/.hive/fleet/group_vars/all
cd ansible
cp inventory.example.yml ~/.hive/fleet/inventory.yml
cp group_vars.example/all/vars.yml ~/.hive/fleet/group_vars/all/vars.yml
The inventory names your machines and marks one as the hub:
hive_hosts:
hosts:
box-a:
box-b:
children:
hive_hub:
hive_hub:
hosts:
hub:
vars:
hive_hub_enabled: true
control:
hosts:
localhost:
ansible_connection: local
Use the names you actually SSH to. The hub sits in its own group that
is a child of hive_hosts, which is what makes it both the
collector and an ordinary agent box.
The vars file says where the boxes get their code:
hive_repo_url: https://github.com/OWNER/hive.git
hive_ref: main
hive_checkout_dir: /srv/lab/hive
hive_hub_host: hub
Warning
hive_repo_url is fetched by the boxes, not by
your laptop. A path that only resolves locally, or a private repository
the boxes can’t authenticate to, fails at the clone step. Whatever you
want running on the fleet has to be pushed to that URL first.
Every command below runs from the ansible directory of
your clone. That is where the configuration lives that points Ansible at
your ~/.hive/fleet/ files.
4.4 Three: the sudo detour, if you need it
Recent Ubuntu releases may route sudo through a
reimplementation that Ansible’s password prompt cannot drive. The
symptom is a converge that hangs at privilege escalation and eventually
times out. Classic sudo is already installed; switch to it
once per affected box, before the first converge:
ssh box-a 'sudo update-alternatives --set sudo /usr/bin/sudo.ws'
4.5 Four: converge
Start with one box you don’t mind breaking, plus your laptop:
ansible-playbook site.yml -K --limit box-a,localhost
-K prompts for your sudo password, which
the first run needs to install packages and create the checkout
directory. Later runs usually don’t.
Note
Including localhost is not decoration. The laptop’s
client files are written by a separate play that only runs when
localhost is in scope. Limit to a box alone and the boxes
get provisioned while your hive command still doesn’t know
they exist.
When that box looks right, do the rest:
ansible-playbook site.yml -K
To see what a run would change without changing it:
ansible-playbook site.yml -K --check --diff
4.6 Five: linger
The daemons run as user services, which by default stop when you log out. Without a setting called linger, your fleet goes quiet every time an SSH session ends — and the symptom is maddening, because boxes appear and disappear depending on whether you happen to be logged in.
The converge enables it, but that step needs a password, so it’s easy
to skip on a run without -K. To be sure:
ssh box-a 'sudo loginctl enable-linger $USER'
4.7 Six: verify
The four checks from Quick Start, in order: the service is
active on each box; it answers on its Tailscale address;
hive status lists everything from your laptop; and a real
session starts and appears.
ssh box-a 'systemctl --user is-active hive-host'
ssh box-a 'curl -s http://$(tailscale ip -4):8788/v1/sessions'
hive status
On the hub, also confirm the collector is up and knows about everyone:
ssh hub 'systemctl --user is-active hive-hub'
ssh hub 'cat ~/.hive/hosts'
4.8 Seven: the phone console
Served by the hub, and deployed separately because it’s built on your laptop and copied over:
ansible-playbook site.yml -K --tags web --limit hive_hub
Then open http://hub/hive/ on a phone that’s on your
tailnet.
4.9 Where everything landed
| Path | Machine |
|---|---|
/srv/lab/hive |
each box: source checkout |
/srv/lab/hive/bin |
each box: the built daemons |
~/.hive/sessions |
each box: one file per session |
~/.hive/host.toml |
each box: bind address and port |
~/.hive/hosts |
hub: generated fleet list |
~/.hive/hub.toml |
hub: collector settings |
~/.hive/client.toml |
laptop: which hub to talk to |
~/.hive/fleet/ |
laptop: your inventory and vars |
Two of these are generated and should not be hand-edited: the hub’s fleet list and the laptop’s copy of it, both written from your inventory on every converge. To add or remove a machine, edit the inventory and converge — editing the generated file works until the next run silently undoes it.
client.toml is the exception. It’s seeded once, when
missing, and then it’s yours; edit it freely to point at a different
hub.
4.10 Adding a box later
One entry in the inventory, then:
ansible-playbook site.yml -K --limit box-c,localhost
The localhost again — without it, the new box provisions
but never appears in your fleet list.
4.11 Pinning a version
hive_ref in your vars file is what the boxes check out.
It takes a branch, a tag, or a commit. Set it to something specific when
you want the fleet to stop tracking your latest push, and converge to
move them.
5 Three Consoles
Everything in this chapter talks to the same hub and sees the same fleet. What changes is where you are and what you’re trying to do.
The three consoles aren’t tiers — there’s no “basic” one and no “pro” one. They’re shaped for different moments. The terminal is for when you’re already in a terminal. The Mac panel is for keeping half an eye on four machines while you work on something else. The phone is for the twenty minutes you’re away from a desk and an agent has gone quiet in a way that might mean it’s finished or might mean it’s stuck.
5.1 The terminal
One command, hive, with subcommands. It reads a two-line
config file naming your hub and holds no state of its own.
$ hive status
box-a -- (no sessions)
box-b -- (unreachable) error sending request for url (...)
*box-a demo running claude /home/you/proj
Running tests now -- 14 passed, 2 failing...
One line per session: host, task, state, which agent, working directory. The indented line underneath is the last thing that agent printed, captured straight from its terminal. A star in the first column means you marked the session as one to watch.
Two rows there aren’t sessions at all. A host with nothing running on
it says (no sessions). A host the hub could not reach at
all says (unreachable), followed by the actual network
error. Both are deliberate — a box that vanishes from a fleet listing is
a box you forget to fix.
The terminal is the right tool when you already have one open, when you want to script something, or when you want output you can pipe. It’s the only console that can start the daemons themselves, and it’s the one that keeps working when you’re SSH’d into a machine somewhere with nothing else available.
5.2 The Mac panel
A native application — not a browser window — that shows your whole fleet and refreshes itself as the hub learns things.
The fleet is a table or a grid of tiles, your choice, and you can narrow it by state, host, agent, or starred. With four boxes and a dozen sessions that filtering stops being a nicety: most of the time you want the three sessions that are actually running and not the nine that finished on Tuesday. Filters persist between launches, as do the theme and text size, because a panel you leave open all day should look the way you like.
Each session offers what makes sense for its state. A running session can be chatted with, attached to, or stopped. A finished one can be resumed. Any session can be restarted, killed outright, or opened as a plain SSH shell in its working directory — useful when you want to look at what the agent did without interrupting it.
5.2.1 Chat is the part that matters
Click a session and you get its conversation, rendered properly: Markdown formatted, mathematics typeset, code in blocks rather than as a wall of escape sequences. Underneath, it’s the same transcript the agent is writing to disk on the box, streamed to you as it grows. There’s a compose box at the bottom. What you type gets typed into the agent’s terminal.
This is a better way to read an agent than attaching to its terminal is. Attaching shows you a scrolling pane you can’t search and can’t scroll far back in. The panel shows you the conversation.
Note
When an agent stops to ask permission — to edit a file, run a command — the panel says so: waiting on a permission prompt. It won’t answer for you. Approving means attaching a real terminal, because the prompt is a menu in the agent’s own interface rather than a message in the conversation. Running Agents covers how to avoid these prompts entirely for sessions you plan to leave alone.
5.2.2 Spawning
The panel’s spawn dialog asks for a host, a directory, a task name, and which agent. The directory field browses the box’s filesystem, not your laptop’s, which spares you guessing at remote paths.
5.3 The phone
Open http://<hub>/hive/ on your phone. That’s the
entire install — it’s a web page the hub serves, so there’s nothing to
keep updated. Add it to your home screen and it behaves close enough to
an app.
You get the fleet as a stack of cards, one per session, filterable by host or down to just the starred ones. Each card carries the session’s state, its agent, and its directory. From a card you can star, chat, stop, restart, or get the SSH command for that session copied to your clipboard.
Chat is the reason to have it. Full-screen, the same rendered conversation the Mac panel shows, live. An agent that’s been running for an hour while you were at lunch can be read and answered from a phone, and that changes which jobs feel safe to start — a long unattended run stops being a commitment to stay near your desk.
You can also start new work from the phone. Spawn is in the header and browses the box’s filesystem the same way the panel does.
Two things the phone deliberately doesn’t do. There’s no terminal in the browser, so attach hands you the SSH command to paste into whatever terminal app you keep on your phone. And there’s no kill — the destructive one is left to the two consoles where you’re less likely to hit it with a thumb.
Note
The phone console is served over plain HTTP on your tailnet, with no login. That’s the same trust model as the rest of hive (The Fleet), but it’s worth noticing on a device that leaves the house. A phone that’s signed in to your tailnet is a phone that can drive your fleet.
5.4 Attach: the escape hatch
Sometimes you need the agent’s actual terminal — to answer a permission prompt, to use a feature the chat view doesn’t expose, to see exactly what it’s showing.
Attach is the one place hive still uses SSH. The hub hands back a command:
ssh box-a -t 'tmux attach -t hive-demo'
What happens next depends on the console. The Mac panel runs it in a
new terminal window — iTerm2 by default, or Terminal, Ghostty, or Warp
if you’d rather. The hive command runs it in place. The
phone copies it for you to paste.
You end up in the agent’s tmux session, exactly as if
you’d SSH’d in yourself. Detach with Ctrl-b d and the agent
keeps running, which is the thing to remember: detaching leaves it
alone, and closing the window is also fine.
Warning
While you’re attached and typing, hive will refuse to send messages
to that session from any console — it checks for recent keyboard
activity and declines rather than paste into the middle of what you’re
doing. If a say from the panel or phone reports that a
human is attached, that human is you, in another window.
5.5 Which one to reach for
| You want to | Reach for |
|---|---|
| See everything at once | the panel |
| Read a long conversation | the panel, or the phone |
| Answer a permission prompt | attach |
| Check in while you’re out | the phone |
| Script it, or pipe it | the terminal |
| Start or install the daemons | the terminal |
Most people use all three within a day and stop thinking about which is which. Starting agents, and deciding what happens when they finish, comes next.
6 Running Agents
Starting an agent takes four decisions: which box, what to call it, which directory, and which agent. Everything else about a session follows from those.
hive run box-a period-index /home/you/lab/period-index
That starts Claude on box-a, in that directory, under
the name period-index. The panel and phone ask the same
four questions in a dialog; the directory field there browses the box’s
own filesystem, which saves you guessing at a remote path.
6.1 The four decisions
Which box. Any box in your fleet. Nothing stops two agents from running on the same machine, and nothing balances them for you — if one box is doing three compiles and another is idle, that’s your call to make.
What to call it. The task name is how you’ll refer to this session forever after, so pick something you’ll recognize in a list next week. Letters, numbers, hyphens, and underscores; no spaces. Names are unique per box, and starting a second session with a live name is refused rather than silently doing something surprising.
Which directory. An absolute path on the box, which must already exist. This becomes the agent’s working directory, and it’s recorded, so resuming later doesn’t require you to remember it.
Which agent. Claude unless you say otherwise:
hive run box-a my-task /home/you/proj --provider codex
6.2 Passing arguments to the agent
Anything after the hive arguments goes to the agent itself, untouched:
hive run box-a my-task /home/you/proj -- --permission-mode acceptEdits
hive doesn’t interpret these. Model selection, permission modes, whatever flags the agent supports — they pass straight through.
6.3 The permission trap
This is the one that costs people an afternoon, so it gets its own section.
A coding agent normally stops and asks before editing a file or running a command. That’s correct when you’re sitting in front of it. It is a disaster for a detached session, because there’s nobody to answer: the agent stops, and it stays stopped. You come back in three hours to an agent that has done nothing since minute four and is still politely waiting.
Warning
A detached agent blocked on a permission prompt waits forever. It does not time out, does not assume yes, and does not fail. Any session you start and walk away from needs the permission question settled before you start it.
Two ways to settle it. Pre-approve in the project’s own agent settings, which is the durable answer for a repository you work in often. Or pass a permission mode at spawn time, which is the quick answer:
hive run box-a my-task /home/you/proj -- --permission-mode acceptEdits
Both consoles will tell you when a session is blocked — the panel and the phone show a waiting on a permission prompt banner in the chat view. Answering requires attaching a terminal, since the prompt is a menu in the agent’s interface rather than a message in the conversation.
6.4 Stop, restart, kill, resume
Four verbs, and the difference between them is what happens to the record.
Stop ends the tmux session and keeps
everything else. The agent is gone; the session’s metadata, its working
directory, and its conversation all remain. It shows as
exited and can be resumed.
Resume starts the agent again, in the same directory, continuing the same conversation — it doesn’t start over. This is why stop is safe: it’s a pause, not a discard.
Restart is stop followed by resume. Use it when an agent is wedged but you want the conversation.
Kill stops the session and deletes the record — metadata, star, everything hive knew about it.
hive stop box-a my-task
hive restart box-a my-task
hive kill box-a my-task
Warning
Kill is not reversible. After it, hive cannot resume that session — the recorded directory and conversation link are gone. The agent’s own conversation file still exists on the box, but reconnecting to it by hand is work (Chat and Transcripts). When you mean “pause,” use stop.
6.5 What happens on the box
Worth knowing, because it explains what survives what.
Starting a session writes a small file describing it and creates a
tmux session running the agent. The agent runs as you, on
that box, with your environment and your logins — hive starts it and
then gets out of the way.
When the agent exits on its own, the tmux session
doesn’t disappear. It drops to a shell with a note saying the agent
exited and the conversation was saved. This is deliberate: it means you
can attach afterwards and look at what happened.
Sessions do not survive a reboot. The tmux
sessions are gone, the records remain, and everything shows as
exited. Resuming them after the box comes back is a
per-session decision — hive won’t do it for you.
6.6 Stars
With a dozen sessions across four boxes, most of them finished, stars are how you keep the two you care about visible.
hive label box-a my-task star
hive label box-a my-task unstar
In the panel and on the phone it’s the star on the session, and both can filter to starred only. The star lives on the box alongside the session’s record, so it’s the same star from every console.
6.7 Looking around a box before you start
To see what’s on a box without SSHing to it:
hive fs box-a --path /home/you/lab
This is the same directory listing the spawn dialogs browse. It’s read-only and it’s for finding the path you want to point an agent at.
7 Chat and Transcripts
Reading what an agent said, and answering it, from a machine that isn’t the one it’s running on.
This works well, and it works by a mechanism worth understanding, because the mechanism has one failure mode that looks like a bug and isn’t. The first half of this chapter is the part you’ll use daily. The second half is what to do when the conversation you’re reading stops being the conversation the agent is having.
7.1 Saying something
hive say box-a my-task --text "run the tests before committing"
In the panel or on the phone, it’s the compose box at the bottom of the chat view.
What happens on the box is exactly what it sounds like: your text is typed into the agent’s terminal and submitted, as though you’d attached and typed it. There’s no separate message channel — hive is driving the same interface you would drive by hand.
That has consequences worth knowing. The session has to be running; you can’t queue a message for an agent that has exited. And because it’s a keyboard, hive clears whatever was half-typed at the prompt before sending, so a message doesn’t get glued onto an abandoned fragment.
Note
If you’re attached in a terminal and actively typing, a
say from another console is refused rather than pasted into
the middle of your input. The message says a human is attached. That
human is you, in another window — detach, or finish typing, and send it
again.
7.2 Reading the conversation
The panel and the phone show the conversation rendered — Markdown formatted, mathematics typeset, code in blocks — and keep it live as the agent works. From the terminal you can fetch it as data:
hive transcript box-a my-task --tail 50
Underneath, none of this is scraped from the terminal. The agent writes its conversation to a file on the box as it goes, and hive reads that file. This is why the chat view can show you a properly formatted conversation with scrollback rather than a screenshot of a pane: it’s reading the real record, not the display.
The consoles keep up by streaming. The box re-reads the file every couple of seconds and pushes anything new through the hub to whoever’s watching, which is why the panel updates on its own without you refreshing. Nothing is sent when nothing has changed.
7.3 When the conversation goes stale
Here’s the part that confuses people.
A session’s record includes an identifier for the conversation file it should read — a binding. Normally hive sets this when it starts the agent and it stays correct for the life of the session.
But the agent can change which conversation it’s in without telling anyone. The clearest case: you attach to the terminal and use the agent’s own resume command to jump to a different conversation. The agent obligingly switches. The session’s record still points at the old one.
Nothing errors. The chat view keeps showing you a conversation, still correctly rendered, live-looking — and it’s the wrong one. Messages you send still go to the terminal and still reach the agent, so the agent responds to you, but you can’t see the response. That asymmetry is the symptom.
Note
The tell: the conversation you’re reading stopped changing, but the session is clearly running and replies to what you send. If the view has simply frozen with the agent frozen too, that’s a stuck agent, not a stale binding.
7.3.1 Fixing it
Ask what’s available:
$ hive bind box-a my-task
box-a my-task bound=3f2a... suggested=9c41... pane=9c41...
* 9c41... my-task 2026-09-16T14:22:08Z live suggested
3f2a... my-task 2026-09-16T11:03:44Z current not the session in the tmux pane
This lists every conversation that could plausibly belong to this
session. The one marked current is what hive is reading
now. The one marked live is what the agent is actually in —
hive determines this by looking at the agent’s own terminal, which is
why it can be confident rather than guessing. The starred line is what
it suggests, and when a live one exists, that’s the suggestion.
Point the session at it:
hive bind box-a my-task --to 9c41...
In the panel and on the phone, the chat view has a Bind button showing the same list with the suggestion highlighted. Picking one updates the session’s record on the box, so every console sees the correction.
Note
Rebinding changes only which conversation hive reads. It does not touch the agent, interrupt it, or alter any conversation. If you pick wrong, pick again.
7.4 When there’s nothing to read yet
A brand-new session has no conversation file until the agent writes one, which it does when it first responds. A chat view that’s empty for the first few seconds of a session’s life is normal. If it’s still empty a minute later and the agent shows as running, attach and look — the usual cause is an agent sitting at a permission prompt — the trap in Running Agents.
8 Keeping It Running
A fleet is a thing you maintain. Machines reboot, versions drift, and something that worked in March stops working in June for a reason that turns out to be three months old.
This chapter is the maintenance you’ll actually do and the failures you’ll actually see.
8.1 Pushing new code
When hive itself changes, the boxes need the new version. They fetch it themselves — so push first, then:
hive-deploy box-a box-b
hive-deploy --all
This pulls the pinned version on each box, rebuilds, and restarts the daemons if the binary changed. It’s a narrower operation than a full converge: no packages, no toolchain, no linger. Day-to-day, it’s the one you want.
Warning
hive-deploy deploys what’s at your configured version
in the remote repository. It does not deploy your working
directory. Changes you haven’t pushed don’t exist as far as the boxes
are concerned, and the symptom of forgetting is a deploy that reports
success and changes nothing.
To preview without changing anything, add -n.
8.2 After a reboot
A box that reboots comes back with its daemon running — that’s what
the service install is for — but with no agents. The tmux
sessions are gone; the records survive. Everything shows as
exited.
Resuming is per-session and deliberate:
hive restart box-a my-task
If a box comes back and the daemon isn’t running, linger is the first thing to check (Standing Up the Fleet).
8.3 The failures you’ll see
8.3.1 A host says (unreachable)
The hub couldn’t get an answer from that box. The error beside it is the real one — read it before guessing.
Three causes, in the order they’re worth checking. The daemon isn’t running:
ssh box-a 'systemctl --user status hive-host'
Or it’s running but the hub is asking the wrong address. The hub’s fleet list holds a Tailscale address per box, recorded at converge time; if a box changed addresses, converge again to regenerate it. Or the tailnet path itself is broken, which you can test from the hub:
ssh hub 'curl -s http://100.64.0.2:8788/v1/sessions'
8.3.2 A host flaps in and out
Reachable, then not, then reachable. This is almost always linger: the daemon is being stopped when your login session ends and started when you log back in. Enable it (Standing Up the Fleet) and restart the daemon.
8.3.3 hive status shows something impossible
Sessions that don’t exist, missing fields, a box reporting oddly.
Usually an old binary — a box running a version of
hive-host that predates a change in what it reports. Deploy
to it.
8.3.4 An agent is running but does nothing
Attach and look. The usual answer is a permission prompt (Running Agents). The second most usual is that the agent finished a while ago and is sitting at a shell, which the session’s last line will show.
8.3.5 The chat view is stale
The conversation stopped updating but the agent replies to what you send: that’s a stale binding, and Chat and Transcripts has the fix.
8.3.6 Everything is unreachable at once
The hub, not the boxes. Check it:
ssh hub 'systemctl --user is-active hive-hub'
The agents are unaffected — this is a blindness, not an outage. If you need one urgently, SSH to its box and attach directly.
8.4 Health at a glance
Ask the hub what it sees:
curl -s http://hub:8787/v1/fleet | jq .
This is exactly what every console renders, and it distinguishes a broken client from a broken fleet: if this is right and your panel is wrong, the problem is the panel.
8.5 Housekeeping
Finished sessions stay in the list until you remove them, and there’s no expiry. Once a week, kill what you’re done with — the record only, the agent is long gone:
hive kill box-a old-task
Star the two or three that matter and filter to starred; a fleet list nobody prunes stops being read.
8.6 Moving the hub
Move the machine into the hub group in your inventory, converge, and
the generated fleet lists follow. Your laptop’s client.toml
does not — it’s seeded once and then yours, so point it at the new hub
by hand.
8.7 What isn’t automatic
Worth saying plainly, because each of these has surprised someone:
Crashed agents are not restarted.
Sessions are not resumed after a reboot.
Stuck sessions raise no alert — nothing watches for an agent that has been waiting since Tuesday.
Old session records are not cleaned up.
Coding agents are not installed, updated, or re-authenticated for you.
hive shows you your fleet and lets you act on it. Deciding what should happen stays with you.
9 Quick Reference
Everything, tersely. The chapters explain; this lists.
9.1 Fleet and sessions
| Command | Does |
|---|---|
hive status |
one line per session, all hosts |
hive run HOST TASK DIR |
start an agent |
hive say HOST TASK --text "..." |
send a message |
hive stop HOST TASK |
end it, keep the record |
hive restart HOST TASK |
stop, then resume |
hive kill HOST TASK |
end it, delete the record |
hive attach HOST TASK |
open its terminal over SSH |
hive label HOST TASK star |
mark as one to watch |
hive label HOST TASK unstar |
unmark |
hive transcript HOST TASK |
conversation as JSON |
hive bind HOST TASK |
list conversation candidates |
hive bind HOST TASK --to ID |
repoint at a conversation |
hive fs HOST |
list a directory on a box |
hive panel |
launch the Mac panel |
9.1.1 Flags
| Flag | On | Meaning |
|---|---|---|
--home PATH |
all | use a different config root |
--provider NAME |
run |
claude (default) or
codex |
--resume |
run |
restart a stopped session |
-- ARGS |
run |
pass the rest to the agent |
--text TEXT |
say |
the message (required) |
--tail N |
transcript |
last N blocks |
--after CURSOR |
transcript |
only what’s new |
--to ID |
bind |
the conversation to bind |
--path PATH |
fs |
directory to list |
9.1.2 Session states
| State | Means |
|---|---|
running |
the agent’s tmux session is
alive |
exited |
record kept, agent gone; resumable |
(no sessions) |
the box is fine and has nothing running |
(unreachable) |
the hub got no answer from that box |
9.2 Daemons
| Command | Does |
|---|---|
hive host serve |
run hive-host in the
foreground |
hive host install |
install and start it as a service |
hive hub serve |
run hive-hub in the
foreground |
hive hub install |
install and start it as a service |
Both take --bind ADDR, --port N, and
--dev. install also takes
--bin PATH.
| Bind address | Accepted |
|---|---|
A Tailscale address
(100.64.0.0/10) |
always |
127.0.0.1 or
localhost |
only with --dev |
0.0.0.0, ::,
* |
never |
9.3 Ports
| Port | Where | What |
|---|---|---|
| 8788 | every box | hive-host |
| 8787 | the hub | hive-hub |
| 80 | the hub | phone console at /hive/ |
| 8789 | anywhere | hive-web, local development
only |
9.4 Configuration files
All under HIVE_HOME, which is ~/.hive
unless the HIVE_HOME environment variable or
--home says otherwise.
| File | Machine | Holds |
|---|---|---|
client.toml |
laptop | which hub to talk to |
hosts |
laptop, hub | the fleet list (generated) |
host.toml |
each box | bind address, port |
hub.toml |
hub | bind, port, poll interval |
web.toml |
hub | phone console server |
sessions/TASK.meta |
each box | one session |
sessions/TASK.labels |
each box | its star |
fleet/inventory.yml |
laptop | your machines, for Ansible |
fleet/group_vars/... |
laptop | shared deploy settings |
9.4.1 Fields
| File | Field | Default |
|---|---|---|
client.toml |
hub |
none; a name or full URL |
client.toml |
terminal |
iterm2,
terminal, ghostty, or warp |
host.toml |
bind |
its own Tailscale address |
host.toml |
port |
|
hub.toml |
bind |
its own Tailscale address |
hub.toml |
port |
|
hub.toml |
poll_secs |
|
hub.toml |
host_port |
9.4.2 The hosts file
One machine per line: a name, optionally followed by where to reach
it. # starts a comment.
box-a http://100.64.0.2:8788
box-b http://100.64.0.3:8788
hub http://100.64.0.1:8788
Generated from your inventory on every converge. Edit the inventory, not this.
9.4.3 A session record
| Key | Is |
|---|---|
task |
the name you gave it |
dir |
working directory on the box |
tmux |
its tmux session name |
started |
when, in UTC |
cmd |
the command line that started the agent |
session_id |
which conversation to read |
provider |
claude or
codex |
9.5 Environment variables
| Variable | Does |
|---|---|
HIVE_HOME |
config root; default
~/.hive |
CLAUDE_BIN |
where claude is, if not on
the usual paths |
CODEX_BIN |
the same for codex |
HIVE_PANEL |
which panel binary hive panel
launches |
Without an override, agents are looked for under
~/.local/bin, then any Node version directories, then the
system paths.
9.6 HTTP API
Every client uses this; it is also what to reach for when diagnosing.
9.6.1 The hub, on 8787
| Endpoint | Returns |
|---|---|
GET /v1/health |
the collector is alive |
GET /v1/fleet |
every host and session, cached |
GET /v1/events |
a stream: something changed |
ANY /v1/hosts/NAME/... |
forwarded to that box |
Anything a box can do is reachable through the hub by prefixing its
path with /v1/hosts/NAME. A client never contacts a box
directly.
9.6.2 A box, on 8788
| Endpoint | Does |
|---|---|
GET /v1/health |
the daemon is alive |
GET /v1/sessions |
every session on this box |
POST /v1/sessions |
start one |
GET /v1/sessions/TASK/transcript |
the conversation |
GET .../transcript/stream |
the conversation, live |
POST .../say |
send a message |
POST .../stop |
end it, keep the record |
POST .../restart |
stop, then resume |
POST .../kill |
end it, delete the record |
POST .../label |
star or unstar |
GET .../bindings |
conversation candidates |
POST .../bind |
repoint at a conversation |
GET .../attach |
the SSH command to attach |
GET /v1/fs |
list a directory |
9.7 Provisioning
| Command | Does |
|---|---|
ansible-playbook site.yml -K |
converge the whole fleet |
... --limit box-a,localhost |
one box, plus client files |
... --check --diff |
show what would change |
... --tags web --limit hive_hub |
deploy the phone console |
hive-deploy box-a |
push code to one box |
hive-deploy --all |
push code to all of them |
hive-deploy -n box-a |
preview that |
Run the playbook from the ansible directory of your
clone.
9.7.1 Tags
| Tag | Step |
|---|---|
packages |
system packages |
toolchain |
Rust |
checkout |
clone at the pinned version |
build |
compile on the box |
hive |
install and start
hive-host |
hub |
install and start
hive-hub |
web |
the phone console |
linger |
let services survive logout (needs
-K) |
clienthosts |
the laptop’s fleet list |
clientconfig |
the laptop’s client.toml |
summary |
report the deployed version |
9.7.2 Deploy settings
In fleet/group_vars/all/vars.yml:
| Setting | Is |
|---|---|
hive_repo_url |
where the boxes clone from |
hive_ref |
branch, tag, or commit to deploy |
hive_checkout_dir |
/srv/lab/hive |
hive_hub_host |
written into the laptop’s config |
hive_host_port |
8788 |
hive_hub_port |
8787 |
hive_web_base_path |
hive, the phone console’s URL
path |
9.8 Building from source
From the repository root:
| Target | Builds |
|---|---|
make build |
the three Rust binaries |
make install |
them, into bin/ |
make install-panel |
the Mac panel |
make install-ops |
hive-deploy onto your
PATH |
make test |
the test suite |
make stack-smoke |
a local hub and box, no tailnet |
make deploy-web |
the phone console |
Or, for the laptop command alone:
cargo install --path crates/hive-cli
9.9 Running it locally
A whole fleet on one machine, for trying things out:
cargo run -p hive-host -- serve --dev --bind 127.0.0.1
cargo run -p hive-hub -- serve --dev --bind 127.0.0.1
printf 'hub = "http://127.0.0.1:8787"\n' > ~/.hive/client.toml
hive status
--dev is what permits a loopback bind. Without it, both
daemons refuse to start.
9.10 When something is wrong
| Symptom | Start with |
|---|---|
One host (unreachable) |
is its daemon running? |
| A host flaps in and out | linger |
| Every host unreachable | the hub’s daemon |
| Stale or impossible sessions | deploy to that box |
| Agent runs but does nothing | attach; likely a permission prompt |
| Chat frozen, agent replies | a stale binding; rebind |
| Converge hangs on the password | classic sudo |
| Session won’t start | is the agent installed on that box? |