● arnab@portfoliotmux 0:zsh*/posts/building-drover-coding-agent-control-plane
● online--:-- ---
arnab@platform:~/posts$cat building-drover-coding-agent-control-plane.mdx
↳ ai · 8 min read

Building Drover: One Interface Across Coding Agents

2026-08-15·by arnab saha·
aiagentsarchitectureioslocal-firstdrover
┌─excerpt─
I wanted the path from thought to execution to stay short, even when the harness, machine, quota window, or session changed.

One provider's quota was running down with a task still on it. Another subscription had room. Getting from one to the other took a terminal, the right machine, a different native harness, and the whole story retold, which is a strange amount of work for what should have been a routing decision.

Earlier this year, I wrote about the first real shift I felt while building with coding agents: the distance between an idea and its execution had started to collapse. I could have an idea while walking, send it from my phone, answer a follow-up, and keep moving. Thought to action no longer had to wait until I was back at my desk.

That was the part I did not want to lose.

#From One Interface to Native Harnesses

For a while, subscriptions and provider authentication were portable enough that I could choose the interaction layer. I could use tools such as Pi or OpenClaw and keep a familiar operating model while changing the model underneath.

That changed as model providers tightened how subscription-backed usage could be accessed. In my setup, using the capacity included with each subscription increasingly meant using its native harness. Third-party tools could still use separately billed APIs, but that defeated the point of having several subscription quota windows available.

The experience became scattered across Claude Code, Codex, Antigravity, and now DeepSeek Harness. Each has its own process, transcript, permissions, authentication, controls, and way of resuming work. The native tools are good, and their differences are often useful. The mental tax comes from changing my interaction model every time I change the harness.

My first common interface was SSH. I would connect to a machine, open the repository, start the native harness, and leave the terminal running. tmux made the sessions durable, but the operating model still lived in my head. I had to remember which machine held the work, which pane contained the session, which provider had capacity, and which terminal needed an approval.

Herdr is a much better version of that terminal-native experience. I used it while building Drover. It keeps real terminal sessions alive, gives coding agents one place to run, and lets me reconnect from another device. But the premise is still the terminal. I can reach it from a phone; the interaction is not designed around a phone.

Claude Code's mobile remote-control experience approaches the problem from the other direction. It feels native on the phone and brings back the short path from thought to action. But it depends on a native Claude Code session already running with remote control enabled. It also solves the mobile experience for one harness, not the rest of the subscriptions and machines in my workflow.

These are good tools solving different slices of the problem. What I wanted was continuity above all of them: one familiar way to start work, see what needs attention, answer a question, and move a task to the harness with available quota without retelling the entire story.

That became Drover.

Drover watching over coding-agent sessions running across several machines

#One Interface, Not One Harness

Drover is not a replacement harness. I did not want to flatten Claude Code, Codex, Antigravity, and DeepSeek into a lowest-common-denominator agent. I want each native harness to keep its own models, tools, permissions, credentials, session behavior, and filesystem access.

The common interface is for me.

On my phone, the questions are usually the same:

  • Which sessions need attention?
  • Which provider still has quota?
  • What question or approval is blocking progress?
  • Can I start the task on the right machine and working directory?
  • Can another harness continue without me retelling the whole story?

Drover keeps those actions consistent. The provider-specific process still runs underneath, with its own credentials, models, tools, and files.

Drover fleet view with live sessions, provider capacity, observed usage, and projectsDrover new-session sheet with host, harness, working directory, model, and reasoning controlsDrover cockpit showing observed activity, token and cost coverage, projects, and configuration insightsDrover analytics showing provider-reported subscription windows and observed token usage

The phone is where I want to make the next decision. The raw terminal remains available when I need it, but it is no longer the only control surface.

#The Command Plane

The live path has three parts:

  1. The iOS app or another authenticated client.
  2. A central drover-server that knows the fleet and routes operations.
  3. A drover-harnessd daemon on each machine that owns the local agent processes.
Drover architecture diagram showing the client, control plane, harness daemon, coding agents, and data layer

The host remains authoritative. A Codex session on Linux uses that machine's checkout and credentials. Claude Code on the Mac Mini runs on the Mac Mini. The central server routes intent and aggregates state, but it does not pretend to own remote processes or files.

Structured adapters normalize the parts that should look familiar: assistant messages, tool calls, results, approvals, questions, cancellation, and lifecycle events. A terminal path remains available because not every interaction fits a stable schema.

The latest adapter is DeepSeek Harness. Drover talks to its loopback Web RPC service, creates or resumes the native session, forwards turns and cancellation, discovers its available models, and normalizes the completed messages and tool calls. Its workspace sandbox is anchored to the working directory selected at launch, so Drover now validates that directory before starting the session.

That is the kind of provider nuance I want the adapter to absorb. I should not need a different operating habit for it.

#Quota and Context Are Routing State

Moving the buttons into one app is not enough. A session is still an island if its identity, history, and limits disappear when the process restarts.

Drover now preserves the provider's native session identity and ordered events. When a daemon restarts, it can recover a structured session instead of presenting it as lost. Handoff briefs and project context retain links to the sessions and events that produced them, so another harness can pick up the work without relying on a vague generated summary.

Quota belongs in that same continuity layer. Drover shows provider-reported capacity windows beside observed activity. It also distinguishes measured API cost from subscription usage where no cost was reported. If one quota window is running down while another has room, I can move the next piece of work deliberately and use the capacity I am already paying for.

I still choose the harness and host rather than letting a heuristic pick the model. Drover removes the context-reconstruction and interaction tax that used to make that choice expensive. Optimizing across subscription quotas should not require resetting my muscle memory or explaining the project from zero.

The context plane stores append-oriented facts in Parquet and uses DuckDB for fleet state, derived views, summaries, and handoff context. MCP tools expose that history back to coding agents when they need to recall a decision or continue earlier work.

#Data Residency Is Part of the Design

Drover is local-first, but I do not mean that every component must forever live under my desk.

Today, the daemons, central server, and data store run on hardware I own. Later, some harness hosts could be remote instances. The boundary I care about is control: I choose where execution happens, where the session data is stored, and how machines join the fleet. The data residency is mine.

Drover stays inside localhost, a private LAN, or Tailscale. It has shell and filesystem authority, so I do not expose it through public ingress. The current security model is one trusted operator, not a hosted multi-tenant control plane.

That constraint keeps the architecture honest about who owns the credentials, processes, and history.

#The Seams Were the Product

The difficult work was rarely the model call.

One early handoff wrote its seed prompt before the new CLI had finished asking whether I trusted the folder. Reconnects duplicated events. A daemon restart made resumable provider sessions look dead. A live snapshot once copied a 483 MB DuckDB database to answer a cockpit request. More recently, a DeepSeek session could launch against the wrong workspace and spend the rest of its life asking for approvals outside its sandbox root.

Those failures shaped the current system: readiness detection, ordered event transcripts, session recovery, host-scoped working-directory suggestions, bounded health checks, and an isolated command-plane store. The abstraction became useful only after it survived those seams.

#What It Proved

Drover now lets me move between native coding harnesses without reorganizing my workflow around each one. I can see capacity, start work on the correct host, intervene from my phone, recover after a restart, and carry context into the next session. I can use the quota available across my subscriptions without paying for every switch with another round of setup and explanation.

The harnesses are not identical, and Drover does not pretend otherwise. The common interface is for me.

When the interaction model, continuity, and routing state live above the individual harness, the thought-to-action barrier can stay small. I do not have to learn and remember every operational nuance before I can act on an idea.


Drover is available at github.com/arniesaha/drover. It currently launches Claude Code, Codex, Antigravity, and DeepSeek Harness across macOS and Linux hosts. OpenClaw, Hermes, and other sources can still contribute activity to the context plane.