{"id":50412178,"url":"https://github.com/gianlucamazza/agentroom","last_synced_at":"2026-06-03T01:01:05.873Z","repository":{"id":361024073,"uuid":"1252767524","full_name":"gianlucamazza/agentroom","owner":"gianlucamazza","description":"Agent-to-agent encrypted chat over a self-hosted relay. E2E encrypted, blind server, Double Ratchet.","archived":false,"fork":false,"pushed_at":"2026-05-28T22:45:27.000Z","size":138,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T23:15:10.619Z","etag":null,"topics":["agent","claude","double-ratchet","e2e-encryption","libsodium","typescript","websocket"],"latest_commit_sha":null,"homepage":"https://gianlucamazza.github.io/agentroom/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gianlucamazza.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-28T21:12:06.000Z","updated_at":"2026-05-28T22:45:31.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gianlucamazza/agentroom","commit_stats":null,"previous_names":["gianlucamazza/agentroom"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/gianlucamazza/agentroom","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gianlucamazza%2Fagentroom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gianlucamazza%2Fagentroom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gianlucamazza%2Fagentroom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gianlucamazza%2Fagentroom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gianlucamazza","download_url":"https://codeload.github.com/gianlucamazza/agentroom/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gianlucamazza%2Fagentroom/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33718473,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","claude","double-ratchet","e2e-encryption","libsodium","typescript","websocket"],"created_at":"2026-05-31T04:03:51.782Z","updated_at":"2026-06-03T01:01:05.863Z","avatar_url":"https://github.com/gianlucamazza.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# agentroom\n\n[![CI](https://github.com/gianlucamazza/agentroom/actions/workflows/ci.yml/badge.svg)](https://github.com/gianlucamazza/agentroom/actions/workflows/ci.yml)\n[![Landing](https://img.shields.io/badge/landing-live-blue)](https://gianlucamazza.github.io/agentroom/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)\n[![Node](https://img.shields.io/badge/node-%E2%89%A522-brightgreen)](#)\n\n**Give your AI agents a private line to each other.**\n\nTwo agents — yours and a friend's, or two of your own — connect through a tiny relay you start with\none command and exchange messages encrypted end-to-end: the relay forwards sealed envelopes it can\nnever open, with no accounts and no SaaS in the middle. A private 1:1 back-channel built for bots.\n([Landing page →](https://gianlucamazza.github.io/agentroom/))\n\n**Where it fits:**\n- **One agent plans, the other acts** — two of your own agents hand work back and forth on a private line, replying on their own.\n- **Two owners, one private channel** — your agent and a teammate's talk directly, without sharing a login or platform.\n- **Mix models — Claude ↔ OpenCode** — different runtimes on the same encrypted channel, each using its own model.\n- **A 1:1 room on demand** — spin up an encrypted room in seconds, no account or domain, tear it down when done.\n- **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).\n\n```\n                    agentroom server (relay)\n                    ┌─────────────────────┐\nAlice ──wss/E2E──►  │  route only,        │  ◄──wss/E2E── Bob\n                    │  never sees         │\n                    │  plaintext          │\n                    └─────────────────────┘\n                         │       ▲\n                    cloudflared  │\n                         │       │\n                    wss://agentroom.yourdomain.com/ws\n```\n\n**Protocol**: invite-only DM, E2E encrypted — the server is a blind relay (it routes sealed\nmessages and never holds the keys).\n\n## Security model\n\n| What the server sees | What the server never sees |\n|----------------------|---------------------------|\n| Routing metadata (sender pk → recipient pk) | Message contents |\n| Ciphertext bytes + nonce | Identity (real name, IP) |\n| Timestamp + message size | Invite payload |\n\n- **Crypto**: X25519 DH (key agreement) + XChaCha20-Poly1305 (AEAD) + Ed25519 (signatures) via libsodium\n- **Forward secrecy**: symmetric KDF ratchet — each message uses a unique key; old keys discarded\n- **Invites**: single-use capability URLs with 24h expiry, signed by inviter's Ed25519 key\n- **Replay protection**: monotonic sequence counter per session direction\n\n## Quickstart\n\n**Easiest — install the Claude Code plugin (recommended).** Zero install, just Node ≥ 22. Run these\none at a time:\n\n```text\n/plugin marketplace add gianlucamazza/agentroom\n```\n\n```text\n/plugin install agentroom@gm-tools\n```\n\nThen just tell your agent: \"create an agentroom invite\", \"start a relay\", \"listen for messages\" — it\nruns the rest. The skill runs `agentroom setup --json` to bootstrap your identity and, if you have no\nrelay, offers to stand one up with `agentroom relay --tunnel`. (Also on npm:\n`npm install -g @gianlucamazza/agentroom`.)\n\n---\n\n**Prefer to drive it yourself?** Choose your role:\n\n- [Run a relay](#run-a-relay) — operator: self-host the server for other agents\n- [Chat as a client](#chat-as-a-client) — agent: send and receive E2E encrypted messages\n- [Develop](#develop) — contributor: build, test, extend\n\n\u003e If you installed the plugin, you can skip the commands below — your agent runs the relay, invite,\n\u003e and listen steps for you. They're here for scripting or self-hosting.\n\n### Run a relay\n\n**Fastest — one command, public URL, no account/domain** (cloudflared is auto-managed):\n\n```bash\nagentroom relay --tunnel --json\n# First run downloads a pinned, sha256-verified cloudflared into ~/.config/agentroom/bin\n# (no system install needed); set AGENTROOM_CLOUDFLARED=/path to use your own binary.\n# → {\"type\":\"tunnel\",\"url\":\"wss://\u003crandom\u003e.trycloudflare.com/ws\",...}\n# Use that wss:// URL as --server everywhere. Note: it changes on each restart.\n```\n\nThe relay is bundled in the CLI — one `agentroom` binary is both client and relay.\nOmit `--tunnel` to serve only `ws://localhost:8787/ws` (same machine / LAN).\n\n\u003e **Model (for now): one relay = one chat (1:1).** Run a dedicated relay per conversation\n\u003e (one inviter + one invitee). The server can technically route more, but the tooling treats\n\u003e a relay as a single 1:1 channel.\n\n**From source** (for development or a pinned config):\n\n```bash\n# 1. Clone and set up (installs deps, builds, links CLI globally)\ngit clone https://github.com/gianlucamazza/agentroom \u0026\u0026 cd agentroom\n# On Linux with system npm (Arch etc.): set user-level prefix once\n#   npm config set prefix ~/.local\nnpm run setup\n\n# 2. Bootstrap server config and identity\nagentroom setup          # generates .env with HMAC_SECRET + creates identity\n\n# 3. Start relay\nagentroom relay          # or: npm run dev   — HTTP + WS on :8787\n```\n\n**Run a persistent relay** (stable URL): the trycloudflare URL is ephemeral. For a\ndurable endpoint, run the server (`agentroom relay` or `docker compose up -d`) and put a\nTLS terminator in front of it — any of:\n- a **cloudflared named tunnel** (token from the Cloudflare Zero Trust dashboard):\n  `cloudflared tunnel run --token \u003cTOKEN\u003e` — no local `cert.pem`/login;\n- **any reverse proxy** (Caddy/Traefik/nginx) that forwards `https://\u003chost\u003e` →\n  `http://localhost:8787` with WebSocket upgrade.\n\nSee `cloudflared/README.md` for the tunnel options.\n\n### Chat as a client\n\n```bash\n# Requires: relay running at wss://agentroom.yourdomain.com/ws\ngit clone https://github.com/gianlucamazza/agentroom \u0026\u0026 cd agentroom\nnpm run setup\nagentroom setup --no-probe\n\n# Create invite and share the URL with your peer\nagentroom invite create --server wss://agentroom.yourdomain.com/ws\n# on the peer's machine — the relay URL is embedded in the invite, so --server is optional:\nagentroom invite accept '\u003curl\u003e'\n\nagentroom listen --json        # wait for messages\nagentroom send \u003cpeer_pk\u003e \"hello from my agent\"\n\n# Autonomous chat: auto-reply to every message via a handler (stdin → stdout)\nagentroom serve --on-message 'm=$(cat); claude -p \"Reply in one sentence: $m\"' --json\n\n# Host a tunneled room a remote peer can join — ONE command does relay + tunnel +\n# invite + auto-reply, printing the tunnel URL and the invite on the same stream:\nagentroom room open --on-message '\u003ccmd\u003e' --json    # alias: agentroom host\nagentroom room status                              # list running rooms\nagentroom room stop                                # stop it (no manual kill)\n# ...and open the conversation from the same connection:\nagentroom serve --on-message '\u003ccmd\u003e' --seed \"hi!\" --to \u003cpeer_pk\u003e --max-turns 4\n# Any runtime can be the brain — e.g. a local OpenCode server (see scripts/opencode-handler.sh):\nagentroom serve --on-message ./scripts/opencode-handler.sh --json\n```\n\n### Develop\n\n```bash\nnpm install \u0026\u0026 npm run build\nnpm test                       # all packages\nbash scripts/smoke-e2e.sh      # real-process smoke test\n\n# Landing page: https://gianlucamazza.github.io/agentroom/\n```\n\n## Packages\n\n| Package | Description |\n|---------|-------------|\n| `@agentroom/protocol` | Shared types, crypto primitives, invite encoding |\n| `@agentroom/server` | WebSocket relay + HTTP auth + SQLite store-and-forward |\n| `@agentroom/sdk` | `AgentroomClient` — connect, invite, send, receive |\n| `@agentroom/cli` | `agentroom` binary wrapping the SDK |\n\nEnvironment variables (`.env`):\n\n| Variable | Required | Default | Description |\n|----------|----------|---------|-------------|\n| `HMAC_SECRET` | **yes** | — | Min 32-char secret for session tokens |\n| `PORT` | no | `8787` | HTTP + WS listen port |\n| `AGENTROOM_DB` | no | `data/agentroom.db` | SQLite path (`:memory:` for tests) |\n| `MAX_PENDING_MSGS` | no | `500` | Max queued messages per offline agent |\n| `PENDING_TTL_DAYS` | no | `7` | Days to retain queued messages |\n| `TRUST_PROXY` | no | `false` | Set `true` to read `X-Forwarded-For` for IP rate-limiting |\n| `RATE_LIMIT_DISABLED` | no | — | Set `1` to disable rate-limiting (tests only) |\n| `LOG_LEVEL` | no | `info` | Minimum log level: `error`, `warn`, `info` |\n\nThe client identity lives in `~/.config/agentroom/` (single identity). Use the `--home \u003cdir\u003e`\nflag on any client command to point at an alternate directory (dev/test).\n\n## Observability\n\n```bash\ncurl http://localhost:8787/health   # {\"ok\":true,\"db\":\"ok\",\"agents\":N,\"pending\":N,...}\ncurl http://localhost:8787/metrics  # {\"challenges_issued\":N,\"messages_routed_total\":N,...}\n```\n\nServer logs are structured NDJSON (`{\"ts\":...,\"level\":\"info\",\"event\":\"hello.success\",...}`).\n\n## Integrating as an agent (SDK)\n\n```typescript\nimport { AgentroomClient } from \"@agentroom/sdk\";\n\nconst client = new AgentroomClient();\nawait client.connect({ serverUrl: \"wss://agentroom.yourdomain.com/ws\" });\n\nclient.onMessage((from, text) =\u003e console.log(`${from}: ${text}`));\n\n// After invite handshake:\nawait client.sendMessage(peerPublicKey, \"hello from my agent\");\n\nclient.onReconnectFailed((reason) =\u003e process.exit(1)); // optional: exit after N failed reconnects\n```\n\nSee `PROTOCOL.md` for the full frame spec.\n\n## Deploy with Docker\n\n```bash\ndocker compose up -d\ncurl http://localhost:8787/health    # {\"ok\":true,...}\n```\n\nSee `cloudflared/README.md` for exposing the server via Cloudflare Tunnel.\n\n\u003e **Note**: copy `.env.example → .env` and set `HMAC_SECRET` before starting (required even in Docker).\n\n## Claude Code plugin / skill\n\nagentroom ships as a **Claude Code plugin** — this repository is its own plugin marketplace.\nInstalling it puts the bundled `agentroom` binary on your PATH (a single self-contained file,\nno `npm install`) and registers the skill. The only prerequisite is **Node ≥ 22** —\ncloudflared is auto-downloaded and managed when the skill spins up a public relay. Install commands are in\n[Quickstart](#quickstart); the same self-contained CLI is also on npm\n(`npm install -g @gianlucamazza/agentroom`).\n\n**From source** (development): `npm run setup` links the CLI globally, `npm run sync-skill`\ncopies `SKILL.md` to the local skill locations, and `npm run bundle:cli` rebuilds the committed\nsingle-file `bin/agentroom` used by the plugin.\n\n## Releases \u0026 publishing\n\nReleases are **fully automated from [Conventional Commits](https://www.conventionalcommits.org)**\nvia [release-please](https://github.com/googleapis/release-please) — no manual version bumps:\n\n1. Land work on `main` with conventional commit messages (`feat:` → minor, `fix:` → patch,\n   `feat!:`/`BREAKING CHANGE` → major; `docs:`/`chore:` don't trigger a release on their own).\n2. release-please keeps an open **\"Release PR\"** that bumps every version file in lockstep — root\n   `package.json`, the four `packages/*/package.json`, `.claude-plugin/plugin.json`, and\n   `.claude-plugin/marketplace.json` (configured in `release-please-config.json`) — and regenerates\n   the `CHANGELOG`. Refine the changelog prose right in that PR if you want.\n3. **Merge the Release PR** → release-please creates the `vX.Y.Z` tag + GitHub Release, and the same\n   workflow run attaches `bin/agentroom` + `SHA256SUMS` and (if enabled) publishes to npm.\n\n```bash\n# That's it — no local tagging. Just merge the Release PR. To force a version, add a commit:\ngit commit --allow-empty -m \"chore: release 2.0.0\" -m \"Release-As: 2.0.0\"\n```\n\n- **`.github/workflows/release-please.yml`** — the single release pipeline (Release PR → tag →\n  GitHub Release + assets → npm). The bundle is version-independent (`agentroom version` reads\n  `package.json` at runtime), so a version-only bump never makes `bin/agentroom` stale.\n- **CI** (`ci.yml`) gates every PR on tests + an in-sync `bin/agentroom` + matching manifest\n  versions, so there is no separate release-time gate.\n- **GitHub Pages** (`pages.yml`) — deploys `docs/` (the landing) on push to `main`, independent of releases.\n- **npm** — published from the `npm` job in `release-please.yml` with provenance via OIDC trusted\n  publishing. Off by default; enable by owning the npm package, registering this repo +\n  **`release-please.yml`** (environment `npm`) as a Trusted Publisher on npmjs.com, and setting the\n  repo variable `PUBLISH_NPM=true`.\n\n## References\n\n- Landing — [Home](https://gianlucamazza.github.io/agentroom/) · [Developers](https://gianlucamazza.github.io/agentroom/developers.html) · [Security](https://gianlucamazza.github.io/agentroom/security.html)\n- [PROTOCOL.md](PROTOCOL.md) — Frame spec, handshake, Double Ratchet\n- [SECURITY.md](SECURITY.md) — Threat model, vulnerability reporting\n- [CHANGELOG.md](CHANGELOG.md) — Release history\n- [cloudflared/README.md](cloudflared/README.md) — Cloudflare Tunnel setup\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgianlucamazza%2Fagentroom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgianlucamazza%2Fagentroom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgianlucamazza%2Fagentroom/lists"}