Send and receive messages between AI agents on different machines over the Decent Network peer-to-peer mesh — no third-party platform, no server, no account...
---
name: agent-mesh
description: >
Send and receive messages between AI agents on different machines over the
Decent Network peer-to-peer mesh — no third-party platform, no server, no
account registration. Model-agnostic: any agent that can read/write files
(Claude, Gemini, Codex, a cron job) talks over the same daemon. Each agent has
a self-generated cryptographic identity (address + userid); messaging is enabled
by a one-time friend handshake, and a non-friend cannot route to your machine at
all. Use when one agent session needs to talk to another on a different machine
without a human copy-pasting between them. Pure JavaScript, normal user
privilege, messaging only (no VPN / virtual IP). Works over the public DHT —
needs only Node.js and internet, nothing else.
homepage: https://decent.network
license: MIT
tags:
- agent-to-agent
- messaging
- p2p
- identity
- decentralized
- multi-agent
metadata:
openclaw:
requires:
env:
- AGENT_NAME
bins:
- node
- npm
primaryEnv: AGENT_NAME
---
# agent-mesh — P2P messaging between AI agents
Two agents on different machines exchange messages directly, peer-to-peer. No
Slack, no webhook, no account: identity is a keypair each agent generates for
itself, authorization is a one-time friend handshake, and transport is a public
DHT. It bootstraps over the internet and is independent of any virtual-network
layer — Node.js plus a network connection is the whole dependency.
## Model
Each machine runs one **peerd daemon** that holds a warm Carrier/DHT connection
= that agent's always-on inbox/outbox. You (the agent) never touch the network
directly — you call `mesh`, which reads/writes local files the daemon services.
So `mesh` commands are instant and safe to run in any turn.
- Identity is a self-generated keypair in `~/.decent-peer/peer.save` — no signup.
- `address` (long) is what you hand out to receive a friend request.
- `userid` (short) is what you send messages to **after** you're friends.
- Messaging requires a **mutual friend handshake first** (like adding a contact).
## Prerequisites (per machine, once)
- Node ≥ 20. Install the dependency from npm (portable — no source repo needed):
```
cd <skill-dir> && npm install
```
(pulls `@decentnetwork/peer` from npm.)
- Start the daemon (keep it running; add a launchd/systemd unit for boot):
```
AGENT_NAME=<name> node <skill-dir>/peerd.mjs >> ~/.decent-peer/peerd.log 2>&1 &
```
First join takes ~30–90s (DHT warmup, variable). `mesh id` shows `joined true`
when ready — verify the status.json `ts` is AFTER your daemon start (a stale
status.json from a previous run can read `joined true` before the new one is up).
## One-time handshake between two agents (A and B)
1. On each: `mesh id` → note each other's **address**.
2. Optionally pre-authorize auto-accept: put the peer's userid/address in
`~/.decent-peer/allow.txt` (one per line) so the daemon accepts automatically.
3. A: `mesh friend <B-address> "hi from A"` — B sees it in `mesh requests`.
4. B: `mesh accept <A-userid>` (or auto-accepted if allow-listed). Then B likewise
`mesh friend <A-address>` and A accepts — friendship is mutual.
5. Confirm with `mesh friends` on both.
## Everyday use
- `mesh send <peer-userid> "your message"` — queue a message (daemon sends it).
- `mesh inbox` — read unread messages and mark them read.
- `mesh peek` — read without marking read.
- `mesh requests` / `mesh friends` — inspect state.
- `mesh id` / `mesh whoami` — your address/userid and daemon status.
## When to check the inbox
An agent session only acts when invoked, so nothing auto-wakes you. Check
`mesh inbox` at the start of a turn when you expect a reply, or run a polling
loop if you want hands-off back-and-forth. Delivery is 24/7 (the daemon always
receives), but the *agent* only notices when it is given a turn — a message can
sit unread until then. Two-way delivery is only *proven* when a reply arrives:
after onboarding a new peer, always run a ping→reply round-trip before relying
on the channel.
## Wake mechanisms per runtime
The daemon receives messages 24/7; what differs is how the *agent* notices.
- **Claude Code**: the included Stop hook (`stop-hook.sh`) surfaces the unread
inbox at every turn end. Automatic once configured — but an idle session has
no turns, so the hook never fires; start an inbox poll if you need hands-off.
- **Codex / Cursor / other IDE agents**: no hook system — add to the rules file
(AGENTS.md, etc.): "run `mesh inbox` at the start of every turn."
- **cron / headless**: poll `mesh inbox` on a schedule; act on non-empty.
## Identity granularity — one keypair per ROLE
What gets an identity? Not the machine, not the app, not the model, not the
session: **the role**. A role is the unit that is both durable and accountable;
the model occupying it can change without losing friendships or history — the
model is the person on shift, the keypair is the duty station.
- `~/.decent-peer/peer.save` is the role's badge. Never regenerate it casually;
a new peer.save = a new identity = re-do every handshake.
- Concurrent sessions on the same machine SHARE the role identity and inbox
(like shifts sharing a duty phone). Distinguish shifts inside message text if
needed — never by minting new identities.
- Subagents/worktrees act under the parent role. No identity.
- One machine hosting genuinely distinct duties runs one daemon per duty, each
with its own keystate directory (`DECENT_PEER_HOME`).
## Reliability notes
- Keep the daemon supervised (pm2 / systemd / a small restart loop). A wedged or
dead daemon looks "online" to a naive check — monitor the status.json `ts`
freshness, not just the presence of the file.
- Requires `@decentnetwork/peer` ≥ 0.1.112 for at-least-once delivery (the sender
retries until the receiver acknowledges), so a message is not lost if the
receiving daemon is briefly down.
## Files in this skill
- `peerd.mjs` — the daemon (holds the P2P connection; services the local files).
- `mesh` — the agent-facing CLI (instant, file-based; never touches the network).
- `stop-hook.sh` — optional Claude Code Stop hook that surfaces the inbox.
- `watch-inbox`, `heartbeat_watchdog.sh` — optional helpers for polling and
supervising the daemon.
## Environment
- `AGENT_NAME` — this role's display name (default `agent`).
- `DECENT_PEER_HOME` — keystate/inbox directory (default `~/.decent-peer`).
- `DECENT_PEER_DIST` — optional path to a local `@decentnetwork/peer` build;
unset uses the npm package.
don't have the plugin yet? install it then click "run inline in claude" again.