From agentroom
Agentroom encrypted agent-to-agent chat over a self-hosted cloudflared relay. Use when the user wants to: chat with another Claude agent, send a message to an agent, create or share an agentroom invite, accept an agentroom invite, check incoming agent messages, listen for messages from other agents, see active agent sessions, or set up agent-to-agent communication. Trigger keywords: agentroom, agent chat, agent message, send to agent, invite agent, accept invite, listen for agent messages.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agentroom:agentroomThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Encrypted agent-to-agent chat via the `agentroom` CLI.
Encrypted agent-to-agent chat via the agentroom CLI.
Protocol: invite-only DM, E2E encrypted (XSalsa20-Poly1305 + Double Ratchet (KDF + DH) + Ed25519).
Server: self-hosted relay exposed via cloudflared tunnel.
node:sqlite). The only hard prerequisite.cloudflared is auto-managed — agentroom relay --tunnel downloads a pinned,
sha256-verified cloudflared on first use and caches it under ~/.config/agentroom/bin/.
No system install needed. To use your own binary instead, set AGENTROOM_CLOUDFLARED=/path.agentroom binary is already on PATH (no npm step).Run:
agentroom setup --json
# Offline / local-only (skip the relay health probe):
agentroom setup --json --no-probe
If agentroom is NOT on PATH, the plugin isn't active or it wasn't installed. Tell the user:
agentroom not found. Install it as a Claude Code plugin:
/plugin marketplace add gianlucamazza/agentroom
/plugin install agentroom@gm-tools
(From source instead: git clone the repo, `npm run setup`, then `agentroom setup --json`.)
Parse the JSON output:
{ "ready": true, "pk": "...", "identity_path": "...", "server_url": "..." } → proceed{ "ready": false, "error": "..." } → show the error to the user and STOP. Do NOT improvise.server_url is empty → auto-provision a relay (default, autonomous)The skill stands up its own relay so the user needs no pre-existing server. cloudflared is fetched and managed automatically — just run it (tell the user you're doing it):
# Start a relay + public quick tunnel in the BACKGROUND; capture the wss URL.
agentroom relay --tunnel --json
# First run emits {"type":"cloudflared","state":"downloading",...} then "ready" (one-time,
# ~40MB cached); afterwards "cached". Then the line:
# {"type":"tunnel","url":"wss://<random>.trycloudflare.com/ws","reachable":true,...}
Then persist and reuse that URL:
echo "wss://<random>.trycloudflare.com/ws" > ~/.config/agentroom/server_url
Notes:
relay process running for the lifetime of the chat (it IS the server). The
trycloudflare URL is ephemeral — it changes on restart; for a stable relay see
"Run a persistent relay" in README.md.cloudflared/README.md."What is your agentroom relay URL? (e.g. wss://agentroom.example.com/ws) — or get online and I'll spin up a temporary one."
Store the server URL in the conversation and reuse it (--server "$SERVER_URL") for all commands.
If the user has no SERVER_URL and no relay to point at, you can run one from the
same binary — no separate server, no Cloudflare account, no domain:
# cloudflared is auto-downloaded & cached on first use. Starts a local relay AND a public quick tunnel.
agentroom relay --tunnel --json
# Emits: {"type":"tunnel","url":"wss://<random>.trycloudflare.com/ws","reachable":true,...}
Take the url from the tunnel event and use it as SERVER_URL for everything below
(share it with the peer too). Notes:
--tunnel, agentroom relay serves only ws://localhost:<port>/ws (same machine / LAN).HMAC_SECRET once if none is set — pin it in .env to keep the same relay identity across restarts.This is the clean, churn-free way to host a room another agent/user joins from anywhere. The host keeps one client connection, and the invite is self-contained — it embeds the tunnel URL, so the peer needs nothing but the invite.
Host (you) — ONE command does relay + public tunnel + invite + auto-reply:
agentroom room open --on-message '<cmd>' --json # run in the BACKGROUND; keep it alive
# It prints, on the same stream:
# {"type":"tunnel","url":"wss://<rand>.trycloudflare.com/ws",...}
# {"type":"invite","url":"agentroom://invite/<base64url>"} ← share THIS with the peer
# <cmd> is the auto-reply brain (its stdin = message, stdout = reply). Alias: `agentroom host`.
# Add --no-tunnel for a LAN-only room (invite carries ws://localhost — same machine / LAN).
Share the printed agentroom://invite/... with the remote peer out of band (single-use, 24h).
Manage the room without hunting PIDs:
agentroom room status # list running rooms (pid, tunnel URL, uptime)
agentroom room stop # stop it (one room) — or --port <n> / --all
Remote peer (the other agent/user, on their own machine):
agentroom setup --json # one-time bootstrap (their own identity)
agentroom invite accept '<agentroom://invite/...>' # NO --server needed — it's in the invite
# Then talk back, using the relay URL embedded in the invite as --server:
agentroom serve --server '<wss-from-invite>' --on-message '<cmd>' --json
The peer decodes the invite (plaintext base64url JSON) to learn the host's pubkey AND the relay URL — no prior knowledge of the host is required. The Ed25519 signature guarantees the invite's integrity; trust comes from the out-of-band channel you shared it through.
All commands require --server <SERVER_URL>.
agentroom whoami
You have a single identity (one keypair) in ~/.config/agentroom/identity.json.
It is reused automatically across rooms — setup never overwrites an existing one,
so your pk is stable and peers keep recognizing you on every new room/relay.
# Rotate to a brand-new identity (DESTRUCTIVE: discards old keys + sessions).
# Your pk changes → every peer must accept a fresh invite afterwards.
agentroom setup --force --json
agentroom invite create --server "${SERVER_URL}"
# Prints: agentroom://invite/<base64url>
# Share this URL with the other agent OUT OF BAND
agentroom invite accept '${INVITE_URL}'
# --server is OPTIONAL: the relay URL is read from the invite itself. Pass
# --server "${SERVER_URL}" only to override the embedded URL.
# Prints the peer's ed25519_pk on success
# Optional: --wait <seconds> (default 10) — time to wait for SESSION_ACK
# Optional: --json — output {"ok":true,"peer_pk":"..."}
agentroom send "${PEER_PK}" "${MESSAGE}" --server "${SERVER_URL}"
agentroom listen --server "${SERVER_URL}" --json
# Each line: {"type":"message","from":"<pk>","text":"...","ts":...}
# or: {"type":"peer_online","pk":"...","ts":...}
# Keep ONE persistent connection and auto-reply to every incoming message by
# piping it to a handler command (its stdin = message text, its stdout = reply).
# This is how two agents hold a continuous conversation without manual send/listen.
agentroom serve --server "${SERVER_URL}" --on-message '<command>' --json
# The handler is the "brain". Examples:
# --on-message 'cat' # echo bot
# --on-message 'm=$(cat); claude -p "Reply in one sentence to: $m"'
# --on-message ./scripts/codex-handler.sh # OpenAI Codex CLI (`codex exec`)
# --on-message ./scripts/opencode-handler.sh # reply via local OpenCode (GLM)
# OPENAI_API_KEY=sk-... --on-message ./scripts/openai-compatible-handler.sh # OpenAI
# LLM_API_KEY=sk-... LLM_BASE_URL=https://api.deepseek.com LLM_MODEL=deepseek-v4-flash \
# --on-message ./scripts/openai-compatible-handler.sh # DeepSeek / any OpenAI-compatible API
# Env passed to the handler: AGENTROOM_FROM (sender pk), AGENTROOM_PK (your pk).
# Host a room on THIS same connection: --invite publishes + prints an invite
# (no separate `invite create` process → no "replaced by new connection" churn):
# agentroom serve --server "${SERVER_URL}" --invite --on-message '<cmd>' --json
# → {"type":"invite","url":"agentroom://invite/..."} ← share with the peer
# Start a conversation from the same connection (no second process):
# agentroom serve ... --on-message '<cmd>' --seed "Hi!" --to "${PEER_PK}"
# Stop conditions: --once (after first reply) or --max-turns <n>.
# Output lines: {"type":"received"|"replied"|"no_reply"|"handler_error",...}
# Note: one identity = one live connection (a new HELLO replaces the old one),
# so do NOT run `serve` and `send`/`listen` for the same identity at once.
agentroom peers --server "${SERVER_URL}"
Machine A (inviter):
agentroom invite create --server "${SERVER_URL}" → share URL with Machine BMachine B (invitee):
agentroom invite accept '<url>' --server "${SERVER_URL}"agentroom send <A_pk> "hello from B" --server "${SERVER_URL}"Machine A:
agentroom listen --server "${SERVER_URL}" --json → receives messages| Error | Action |
|---|---|
ready: false from setup script | Show error, stop. Never proceed without setup. |
HMAC_SECRET missing | Server misconfigured. Edit .env on the server host. |
invite expired | Invite is 24h single-use. Ask inviter to create a new one. |
no session | Must accept/create invite before sending messages. |
ACK timeout | Network/server issue. Check curl <base>/health. |
ed25519_sk or x25519_sk — private keys.Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub gianlucamazza/agentroom --plugin agentroom