Tony (chief of staff agent) was asked to send a task to Mel (engineering agent on a separate VPS) using a custom HTTP setup built over Tailscale. The setup was real, working, and documented in the agent registry.
Tony’s response:
“No nodes showing up. The Tailscale node pairing might not be configured yet on my end.”
Wrong feature. The right one didn’t exist — because OpenClaw doesn’t have built-in agent-to-agent messaging.
What Nodes Actually Are
OpenClaw nodes are peripheral devices — macOS, iOS, Android, or headless Linux machines — that connect to a Gateway and extend what it can reach. Camera. Screen capture. Sensors. Remote shell execution via system.run.
The architecture is Gateway-centric: the model talks to the Gateway, the Gateway routes tool calls to the connected node. Nodes are “peripherals, not gateways” — they don’t run their own gateway service. They extend the one that’s already running.
So yes, a node can run a shell command on a remote machine. But that’s not the same as two agents exchanging structured messages. There’s no TASK payload, no STATUS response, no queue. A node gives your agent a longer arm — it doesn’t give it a correspondent.
Why the Agent Got Confused
The confusion is understandable. Ask an agent to “send a message to another machine over Tailscale” and it reaches for the native feature that connects machines. Nodes are that feature. The mapping is wrong but the logic is sound.
This is the general failure mode: the agent pattern-matches on concept rather than reading the specific mechanism you built. “Another machine” → nodes. Never mind that nodes solve a different problem.
The Fix
Two changes. Both in workspace files read at startup.
1. Add an explicit correction to AGENTS.md:
| 2026-03-04 | Sending tasks to @Mel does NOT use OpenClaw nodes/node pairing.
Nodes are for device peripherals (camera, shell, screen) — not agent messaging.
Use a plain curl POST: `source ~/.env && curl -s -X POST "$MEL_URL/message"
-H "X-Shared-Secret: $SHARED_SECRET" -H "Content-Type: application/json"
-d '{"type":"TASK","payload":{...}}'`. Credentials in .env. No pairing needed. |
Three elements that matter:
- “does NOT use nodes” — rules out the native path
- A one-line explanation of what nodes actually are — closes the conceptual gap so the agent doesn’t re-derive the wrong answer
- The exact curl command — nothing left to interpret
2. Name the section in your registry explicitly:
### @Tony → @Mel: Exact Command ← not "Sending a Message"
“Exact Command” signals there’s something specific to run here, not something to reason about.
Building Agent-to-Agent Messaging
Since OpenClaw doesn’t provide this natively, here’s the pattern that works:
Receiving agent ( Mel’s machine) runs a FastAPI server bound to a private network interface:
@app.post("/message")
async def receive_message(payload: dict, x_shared_secret: str = Header(None)):
if x_shared_secret != SHARED_SECRET:
raise HTTPException(status_code=401)
await handle(payload)
return {"status": "received"}
Bind to your Tailscale IP, not 0.0.0.0. Port only reachable from the tailnet.
Sending agent ( Tony’s machine) posts via shell:
source ~/.env
curl -s -X POST "$MEL_URL/message" \
-H "X-Shared-Secret: $SHARED_SECRET" \
-H "Content-Type: application/json" \
-d '{"type": "TASK", "payload": {"objective": "..."}}'
Both agents have shell access. That’s the whole transport layer.
For the return direction — Mel sending status back to Tony — configure OpenClaw’s remote gateway:
openclaw config set gateway.remote.url wss://tony-tailscale-hostname
openclaw config set gateway.remote.token your-gateway-token
Then from Mel’s machine:
openclaw agent --agent main -m '{"type":"STATUS","from":"mel","payload":{...}}'
Key Takeaway
OpenClaw nodes are for extending your gateway’s reach to hardware — camera, screen, remote shell. They’re not an agent messaging bus. If you need two autonomous agents on separate machines to exchange structured messages, you build that: FastAPI on the receiver, curl on the sender, shared secret for auth.
The agent will reach for nodes when you describe the problem in terms of “other machine.” The fix is workspace instructions specific enough to name the mechanism, explain why nodes don’t apply, and include the exact command — so the agent intercepts the wrong path before it starts walking it.