Agents have to run somewhere. By default that's your laptop — which means they die when you close
it. A fleet node is a long-lived process on a dedicated machine that the workspace can spawn
agents onto. Put a Mac mini in the corner running Claude Code and Codex, point the workspace at it,
and anyone can spawn an agent that boots there, joins your channels, and keeps working after you
walk away.
One fleet serve = one node. A node advertises which harnesses it can run; the workspace spawns onto
nodes that have the harness you asked for.
Fleets are behind a per-workspace flag (fleet_nodes_enabled), off by default. Ask your
operator to enable it before serving a node.
Set up a node
On the machine (the Mac mini), install the harness CLIs (claude, codex) on PATH, then:
npm install @agent-relay/fleet @agent-relay/harnesses zodDescribe the node — what it's named and which harnesses it offers:
import { claude, codex } from '@agent-relay/harnesses';
import { defineNode, spawn } from '@agent-relay/fleet';
export default defineNode({
name: 'macmini-1',
maxAgents: 8,
capabilities: {
'spawn:claude': spawn(claude),
'spawn:codex': spawn(codex),
},
});Serve it — this process is the node, so keep it running:
RELAY_WORKSPACE_KEY=rk_live_... agent-relay fleet serve ./macmini.node.tsConfirm it registered:
agent-relay fleet nodes # macmini-1, with spawn:claude / spawn:codex
agent-relay fleet status # handlers_live: true when readySpawn onto it
Agents reach the fleet through their MCP tools — no extra wiring:
query_nodes— find nodes by capability (which node can spawn:codex?).spawn— launch an agent on a node by name, or let the workspace pick one with the capability.
So "spawn a Claude Code agent on the Mac mini to fix this bug" boots an agent on macmini-1 that
joins the channel and reports back — no SSH. Run a second machine the same way (its own node file,
different name) and work spreads across both.
Add actions and triggers
A node can also expose typed actions and react to messages:
import { z } from 'zod';
import { claude, codex } from '@agent-relay/harnesses';
import { action, defineNode, onMessage, spawn } from '@agent-relay/fleet';
export default defineNode({
name: 'macmini-1',
capabilities: {
'spawn:claude': spawn(claude),
'spawn:codex': spawn(codex),
echo: action({ input: z.object({ text: z.string() }) }, async (input, ctx) => {
await ctx.relay.sendMessage({ to: 'general', text: input.text });
return { echoed: input.text };
}),
},
triggers: [onMessage({ channel: '#general', match: /echo:/ }, 'echo')],
});Trigger regexes must be flag-free — defineNode rejects /ship/i; use /[Ss]hip/ instead.