I use NanoClaw as my agent orchestration layer. Channels, routing, approvals, agent-to-agent delivery: that switchboard is the part I trust and want to keep. What I did not want was to be stuck forever on “the agent is a Docker container on the same machine as the host.”
That is the default today, and it is a good default. The host and the agent share a filesystem for the session mailbox. Wake means docker run on that box. I wrote about why I like that isolation posture in why I chose NanoClaw. The problem is the assumption underneath it: co-location. If orchestration only works when every agent is a local container, I cannot put the heavy work on thin cloud machines, and I cannot meet the community where they already are.
The NanoClaw community has been clear about wanting more than local Docker. There are asks for a native runner that bypasses Docker (despite the clear security weakness of dropping the container boundary), people who just want to run without Docker at all, Apple Container work including a related PR, and a Kubernetes runtime for agent pods. I am interested in those paths too, but the one I personally wanted is agents on thin cloud clients like Fly Machines: a slim always-on host for chat and routing, and agent VMs that only bill while they are awake. Stuffing Docker-in-Docker onto one fat Fly VM is not the product I want. I want a pluggable agent container abstraction.
So I built abstractions as packages, the same way I did for hosthooks, webchat, and friends. Not upstream PRs that turn every experiment into a core commit. Installable skills that leave the trunk small.
Running an agent on a different machine than the host sounds like a spawn problem. It is not, at least not first. Today the session mailbox is a shared filesystem: inbound and outbound databases, heartbeats, attachments. That only works when both sides can see the same disk. Put the agent on another machine and there is no shared directory left. Before a remote agent container runtime is useful, you need a mailbox that works across machines.
Split the mailbox from the agent container
Two different problems get tangled if you solve them in one place.
The mailbox is how the host and agent exchange messages, heartbeats, and attachments. Today that is files under a shared session directory. Fine when both sides share a disk. Useless when the agent is on another machine.
The agent container is how you wake and kill the unit that runs the agent. In today’s NanoClaw core that is Docker on the host. This is a great solution for local isolation, but we need an abstraction if we want the agent group to run in some other kind of container or on a different physical machine. Different containers we might want are native Apple containers, remotely hosted containers like Fly Machines, or even a raw child process which can be thought of as a container too, just one without strong boundaries.
nanoclaw-sessionio is the mailbox abstraction. It installs a SessionTransport registry. Filesystem stays the default, so installing the package alone does not change your desk. An HTTP transport moves the same operations when there is no shared mount. The registry is the point: filesystem and HTTP are the first two transports, not the last. The same abstraction can grow toward cloud messaging queues, RabbitMQ, SES, or whatever wire makes sense for a given deploy, without teaching core a new protocol each time.
nanoclaw-agenthosts is the agent container abstraction. It installs a RuntimeDriver registry. Docker stays the default wake and kill path. Other drivers register beside it. Core still owns policy: router, channels, a2a guards, destinations, approvals. Packages only swap pipes and which kind of container the agent runs in.
If that sounds like hosthooks again, it should. Sessionio and agenthosts are the substrates. The agenthost-* packages are the product plugins, the way webchat hangs off hosthooks.
Defaults stay boring on purpose
Install a substrate and nothing should move until you opt a group in. Filesystem mailbox. Docker runtime. Same sleep posture. The blast radius of “I installed a package” should not be “every agent now spawns somewhere else.”
Co-located alternatives first
Not every alternate host is remote. Raw process and Apple Container still share a disk with the host, so they need a RuntimeDriver, not a network mailbox.
nanoclaw-agenthost-process is the first of those: spawn the agent as a local child process, no Docker. I am shipping it with eyes open. You lose the container boundary that sold me on NanoClaw in the first place. Some operators want that trade anyway for simpler local runs, and the community has been clear about it. The package makes the trade explicit instead of pretending Docker was never a choice.
An Apple Container driver fits the same slot later. Same rule: co-located spawners use the filesystem transport. They do not need HTTP sessionio unless you are testing multi-host IO on one machine.
What I actually want: Fly Machines
Remote agents need both abstractions. No shared mounts means filesystem transport with a Fly runtime is illegal. Fail closed at resolve and wake. Do not discover that by watching a Machine come up with nowhere to write outbound messages.
The path I am on:
- sessionio with filesystem still the default, plus HTTP so mailbox ops can cross hosts while Docker still wakes locally for testing.
- agenthosts with Docker still the default, plus rejection of bad transport/runtime pairs.
- OneCLI reachable from the agent network. Agents still egress through the credential proxy. Where OneCLI lives is flexible (cloud, remote self-host, or a process the agent can dial). The knob that bites people is
GATEWAY_BASE_URL: it has to be an address those agents can reach, not whatever made sense forhost.docker.internalon a laptop. nanoclaw-agenthost-flyioto wake a Machine, get a reply, and idle-stop. Credentials still go through OneCLI. No raw API keys in agent env.
That is the shape I care about for the business: NanoClaw as orchestration, Fly (and later peers) as elastic agent compute. Keep the host thin. Pay for agent CPU when the work is happening.
Per group, like provider
One install can register many RuntimeDrivers at once. Which one you use is per agent group, the same mental model as provider today.
Install has: docker (default) + process + fly
Group A: runtime=docker
Group B: runtime=process
Group C: runtime=fly (needs session_transport=http)
Set it at create or config time and persist it. After a host restart or an idle sweep, the host has to know which driver owns that session. “Spawn with runtime X just this once” without writing the group config is how you get zombies.
Fly refuses filesystem transport. Process defaults to filesystem and ignores HTTP unless you are testing.
How to add the next host
If the next ask is Kubernetes, Railway, Modal, or something I have not thought of yet, the loop is the same. Packages, not a standing fork of core.
- Decide co-located vs remote. Co-located: filesystem mailbox is enough. Remote: depend on sessionio HTTP.
- Implement a RuntimeDriver against agenthosts: wake, kill, is-running, and any machine id you need to cache.
- Ship an npm package with the usual installer:
sync-skill, idempotent host patches, boot registration, verify steps. - Fail closed on illegal transport/runtime pairs, with an error a human can read.
- If agents make credentialed model calls from that network, document how they reach OneCLI.
Core stays the switchboard. Everything is still a message. You only changed how the mailbox moves and what kind of container the agent runs in.
Packages
These are in Labs and on the NanoClaw packages page:
Same delivery model as hosthooks and webchat: skill-installed, MIT, versioned on their own. Prove the abstraction in a package. Keep the host small.
If you want help standing up an agent scaffold that can grow from a laptop desk to remote runtimes without a rewrite, book a call.