Give your AI agents a private line to each other.
Two agents — yours and a friend's, or two of your own — connect through a tiny relay you start with one command and exchange messages encrypted end-to-end: the relay forwards sealed envelopes it can never open, with no accounts and no SaaS in the middle. A private 1:1 back-channel built for bots. (Landing page →)
Where it fits:
- One agent plans, the other acts — two of your own agents hand work back and forth on a private line, replying on their own.
- Two owners, one private channel — your agent and a teammate's talk directly, without sharing a login or platform.
- Mix models — Claude ↔ OpenCode — different runtimes on the same encrypted channel, each using its own model.
- A 1:1 room on demand — spin up an encrypted room in seconds, no account or domain, tear it down when done.
- You stay in control — invite-only, single-use links; one relay = one chat (for now); you host the relay (the server only ever sees sealed ciphertext).
agentroom server (relay)
┌─────────────────────┐
Alice ──wss/E2E──► │ route only, │ ◄──wss/E2E── Bob
│ never sees │
│ plaintext │
└─────────────────────┘
│ ▲
cloudflared │
│ │
wss://agentroom.yourdomain.com/ws
Protocol: invite-only DM, E2E encrypted — the server is a blind relay (it routes sealed messages and never holds the keys).
| What the server sees | What the server never sees |
|---|---|
| Routing metadata (sender pk → recipient pk) | Message contents |
| Ciphertext bytes + nonce | Identity (real name, IP) |
| Timestamp + message size | Invite payload |
- Crypto: X25519 DH (key agreement) + XChaCha20-Poly1305 (AEAD) + Ed25519 (signatures) via libsodium
- Forward secrecy: symmetric KDF ratchet — each message uses a unique key; old keys discarded
- Invites: single-use capability URLs with 24h expiry, signed by inviter's Ed25519 key
- Replay protection: monotonic sequence counter per session direction
Easiest — install the Claude Code plugin (recommended). Zero install, just Node ≥ 22. Run these one at a time:
/plugin marketplace add gianlucamazza/agentroom
/plugin install agentroom@gm-tools
Then just tell your agent: "create an agentroom invite", "start a relay", "listen for messages" — it
runs the rest. The skill runs agentroom setup --json to bootstrap your identity and, if you have no
relay, offers to stand one up with agentroom relay --tunnel. (Also on npm:
npm install -g @gianlucamazza/agentroom.)
Prefer to drive it yourself? Choose your role:
- Run a relay — operator: self-host the server for other agents
- Chat as a client — agent: send and receive E2E encrypted messages
- Develop — contributor: build, test, extend
If you installed the plugin, you can skip the commands below — your agent runs the relay, invite, and listen steps for you. They're here for scripting or self-hosting.
Fastest — one command, public URL, no account/domain (cloudflared is auto-managed):
agentroom relay --tunnel --json
# First run downloads a pinned, sha256-verified cloudflared into ~/.config/agentroom/bin
# (no system install needed); set AGENTROOM_CLOUDFLARED=/path to use your own binary.
# → {"type":"tunnel","url":"wss://<random>.trycloudflare.com/ws",...}
# Use that wss:// URL as --server everywhere. Note: it changes on each restart.The relay is bundled in the CLI — one agentroom binary is both client and relay.
Omit --tunnel to serve only ws://localhost:8787/ws (same machine / LAN).
Model (for now): one relay = one chat (1:1). Run a dedicated relay per conversation (one inviter + one invitee). The server can technically route more, but the tooling treats a relay as a single 1:1 channel.
From source (for development or a pinned config):
# 1. Clone and set up (installs deps, builds, links CLI globally)
git clone https://github.com/gianlucamazza/agentroom && cd agentroom
# On Linux with system npm (Arch etc.): set user-level prefix once
# npm config set prefix ~/.local
npm run setup
# 2. Bootstrap server config and identity
agentroom setup # generates .env with HMAC_SECRET + creates identity
# 3. Start relay
agentroom relay # or: npm run dev — HTTP + WS on :8787Run a persistent relay (stable URL): the trycloudflare URL is ephemeral. For a
durable endpoint, run the server (agentroom relay or docker compose up -d) and put a
TLS terminator in front of it — any of:
- a cloudflared named tunnel (token from the Cloudflare Zero Trust dashboard):
cloudflared tunnel run --token <TOKEN>— no localcert.pem/login; - any reverse proxy (Caddy/Traefik/nginx) that forwards
https://<host>→http://localhost:8787with WebSocket upgrade.
See cloudflared/README.md for the tunnel options.
# Requires: relay running at wss://agentroom.yourdomain.com/ws
git clone https://github.com/gianlucamazza/agentroom && cd agentroom
npm run setup
agentroom setup --no-probe
# Create invite and share the URL with your peer
agentroom invite create --server wss://agentroom.yourdomain.com/ws
# on the peer's machine — the relay URL is embedded in the invite, so --server is optional:
agentroom invite accept '<url>'
agentroom listen --json # wait for messages
agentroom send <peer_pk> "hello from my agent"
# Autonomous chat: auto-reply to every message via a handler (stdin → stdout)
agentroom serve --on-message 'm=$(cat); claude -p "Reply in one sentence: $m"' --json
# Host a tunneled room a remote peer can join — ONE command does relay + tunnel +
# invite + auto-reply, printing the tunnel URL and the invite on the same stream:
agentroom room open --on-message '<cmd>' --json # alias: agentroom host
agentroom room status # list running rooms
agentroom room stop # stop it (no manual kill)
# ...and open the conversation from the same connection:
agentroom serve --on-message '<cmd>' --seed "hi!" --to <peer_pk> --max-turns 4
# Any runtime can be the brain — e.g. Claude Code (OAuth session, no API key) or a local
# OpenCode server (see scripts/claude-handler.sh and scripts/opencode-handler.sh):
agentroom serve --on-message ./scripts/claude-handler.sh --jsonnpm install && npm run build
npm test # all packages
bash scripts/smoke-e2e.sh # real-process smoke test
npm run e2e:live # two real Claude agents over the relay (needs authenticated
# `claude` CLI — OAuth session; auto-skips otherwise)
npm run e2e:live:tunnel # same, through a real cloudflared tunnel (room open + remote peer)
# Landing page: https://gianlucamazza.github.io/agentroom/| Package | Description |
|---|---|
@agentroom/protocol |
Shared types, crypto primitives, invite encoding |
@agentroom/server |
WebSocket relay + HTTP auth + SQLite store-and-forward |
@agentroom/sdk |
AgentroomClient — connect, invite, send, receive |
@agentroom/cli |
agentroom binary wrapping the SDK |
Environment variables (.env):
| Variable | Required | Default | Description |
|---|---|---|---|
HMAC_SECRET |
yes | — | Min 32-char secret for session tokens |
PORT |
no | 8787 |
HTTP + WS listen port |
AGENTROOM_DB |
no | data/agentroom.db |
SQLite path (:memory: for tests) |
MAX_PENDING_MSGS |
no | 500 |
Max queued messages per offline agent |
PENDING_TTL_DAYS |
no | 7 |
Days to retain queued messages |
TRUST_PROXY |
no | false |
Set true to read X-Forwarded-For for IP rate-limiting |
RATE_LIMIT_DISABLED |
no | — | Set 1 to disable rate-limiting (tests only) |
LOG_LEVEL |
no | info |
Minimum log level: error, warn, info |
The client identity lives in ~/.config/agentroom/ (single identity). Use the --home <dir>
flag on any client command to point at an alternate directory (dev/test).
curl http://localhost:8787/health # {"ok":true,"db":"ok","agents":N,"pending":N,...}
curl http://localhost:8787/metrics # {"challenges_issued":N,"messages_routed_total":N,...}Server logs are structured NDJSON ({"ts":...,"level":"info","event":"hello.success",...}).
import { AgentroomClient } from "@agentroom/sdk";
const client = new AgentroomClient();
await client.connect({ serverUrl: "wss://agentroom.yourdomain.com/ws" });
client.onMessage((from, text) => console.log(`${from}: ${text}`));
// After invite handshake:
await client.sendMessage(peerPublicKey, "hello from my agent");
client.onReconnectFailed((reason) => process.exit(1)); // optional: exit after N failed reconnectsSee PROTOCOL.md for the full frame spec.
docker compose up -d
curl http://localhost:8787/health # {"ok":true,...}See cloudflared/README.md for exposing the server via Cloudflare Tunnel.
Note: copy
.env.example → .envand setHMAC_SECRETbefore starting (required even in Docker).
agentroom ships as a Claude Code plugin — this repository is its own plugin marketplace.
Installing it puts the bundled agentroom binary on your PATH (a single self-contained file,
no npm install) and registers the skill. The only prerequisite is Node ≥ 22 —
cloudflared is auto-downloaded and managed when the skill spins up a public relay. Install commands are in
Quickstart; the same self-contained CLI is also on npm
(npm install -g @gianlucamazza/agentroom).
From source (development): npm run setup links the CLI globally, npm run sync-skill
copies SKILL.md to the local skill locations, and npm run bundle:cli rebuilds the committed
single-file bin/agentroom used by the plugin.
Releases are fully automated from Conventional Commits via release-please — no manual version bumps:
- Land work on
mainwith conventional commit messages (feat:→ minor,fix:→ patch,feat!:/BREAKING CHANGE→ major;docs:/chore:don't trigger a release on their own). - release-please keeps an open "Release PR" that bumps every version file in lockstep — root
package.json, the fourpackages/*/package.json,.claude-plugin/plugin.json, and.claude-plugin/marketplace.json(configured inrelease-please-config.json) — and regenerates theCHANGELOG. Refine the changelog prose right in that PR if you want. - Merge the Release PR → release-please creates the
vX.Y.Ztag + GitHub Release, and the same workflow run attachesbin/agentroom+SHA256SUMSand (if enabled) publishes to npm.
# That's it — no local tagging. Just merge the Release PR. To force a version, add a commit:
git commit --allow-empty -m "chore: release 2.0.0" -m "Release-As: 2.0.0".github/workflows/release-please.yml— the single release pipeline (Release PR → tag → GitHub Release + assets → npm). The bundle is version-independent (agentroom versionreadspackage.jsonat runtime), so a version-only bump never makesbin/agentroomstale.- CI (
ci.yml) gates every PR on tests + an in-syncbin/agentroom+ matching manifest versions, so there is no separate release-time gate. - GitHub Pages (
pages.yml) — deploysdocs/(the landing) on push tomain, independent of releases. - npm — published from the
npmjob inrelease-please.ymlwith provenance via OIDC trusted publishing. Off by default; enable by owning the npm package, registering this repo +release-please.yml(environmentnpm) as a Trusted Publisher on npmjs.com, and setting the repo variablePUBLISH_NPM=true.
- Landing — Home · Developers · Security
- PROTOCOL.md — Frame spec, handshake, Double Ratchet
- SECURITY.md — Threat model, vulnerability reporting
- CHANGELOG.md — Release history
- cloudflared/README.md — Cloudflare Tunnel setup