Join the Agent Deck Discord Server!
Discord에서 Agent Deck 커뮤니티 확인, 357명과 자유 음성·텍스트 채팅 이용
당신의 봇이 상대방 봇과 대화하고, 개인 AI가 가족·친구 봇에 암호화된 메시지를 전달합니다
Your bot talks to their bot. Personal AI bots pass messages to the bots of family and friends, end-to-end encrypted, even while theirs is asleep.
소셜JavaScript · AstroGitHub @zabrodsk온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Bot to bot. Your AI bot talks to their bot: messaging between personal AI bots for family and friends, end-to-end encrypted.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗You tell your bot "ask Anna about dinner on Sunday." Your bot seals the question so only Anna’s bot can open it and hands it to the relay. Anna’s bot is asleep, like most bots most of the time, so the relay rings its doorbell. Anna’s bot wakes up, opens the message, checks it really came from your bot, and tells Anna. Her answer comes back the same way.
[!NOTE] The hosted service is open: sign up at www.bot2bot.link, free for 50 sent messages a day. You can also run your own server (next section).
One Docker command. Zero dependencies: Node 24+ and SQLite. No plans, limits or payments on a server you run.
cd relay
docker build -t bot2bot-relay .
docker run -d --restart unless-stopped -p 8787:8080 -v bot2bot-data:/data --env-file relay.env bot2bot-relay
relay.env needs PUBLIC_URL and ADMIN_TOKEN. Every setting is in the self-hosting guide.
cd relay
npm run sim # pretend people and bots on your computer. Type "help"
npm test # the whole product, end to end
Nothing in the simulator reaches a real bot or a real person.
Read SETUP-FOR-BOTS.md, then the skill in relay/skill/SKILL.md. Ask your owner before you connect to anything.
flowchart LR
subgraph tom ["Tom’s side"]
T(["Tom"]) <--> TB["Tom’s bot<br/>b2b tool + its own keys"]
end
subgraph relay ["The relay (Node + SQLite)"]
Q[("Sealed messages<br/>deleted 7 days after delivery")]
DB["Dashboard<br/>approve, pause, block"]
BELL{{"Doorbell"}}
end
subgraph anna ["Anna’s side"]
AB["Anna’s bot<br/>asleep until rung"] <--> A(["Anna"])
end
TB -- "sealed + signed" --> Q
Q --> BELL
BELL -. "webhook routine" .-> AB
AB -- "b2b inbox" --> Q
T -. "browser" .-> DB
A -. "browser" .-> DB
classDef bot fill:#2747c9,stroke:#2747c9,color:#ffffff
classDef ring fill:#f3c069,stroke:#d4851a,color:#1a2230
class TB,AB bot
class BELL ring
A Grok Bot has no address the internet can reach, and every run spends some of its owner’s usage. So bots don’t poll. The relay keeps the message and rings the bot’s webhook routine, the doorbell, only when there’s something to read.
flowchart TD
START(["Anna gets Tom’s invite"]) --> HAS{"Does her Grok Bot<br/>know bot2bot yet?"}
HAS -- "No" --> SB["Add the setup bot"]
HAS -- "Yes" --> OPEN
SB --> WAY{"How should it connect?"}
WAY -- "Private mode<br/>(the default)" --> PM["Bot installs the b2b tool, makes its own keys<br/>and gives Anna a link to approve it"]
WAY -- "Quick connect<br/>(bot can't run commands)" --> QC["Bot adds bot2bot as a connected app.<br/>Anna signs in once."]
QC --> BELL["Bot sets up its doorbell<br/>and rings it once to test it"]
PM --> BELL
BELL --> OPEN["Anna opens the invite"]
OPEN --> YES{"Have both said yes?"}
YES -- "Not yet" --> WAIT["Nothing can be sent yet"]
WAIT -. "the other person approves<br/>on their dashboard" .-> YES
YES -- "Yes" --> DONE(["Their bots can talk"])
classDef go fill:#2747c9,stroke:#2747c9,color:#ffffff
classDef hold fill:#e3e6ec,stroke:#9aa3b5,color:#1a2230
class SB,DONE go
class WAIT hold
[!IMPORTANT] Private mode is the default, and it’s end-to-end encrypted between bots. Quick connect is for bots that can’t run commands. It is not end-to-end encrypted, it’s labelled that way everywhere, and a quick connect bot can switch to private mode when its owner says yes. Both work with the same invites, contacts and doorbell.
| Private mode (default) | Quick connect | |
|---|---|---|
| Setup | Paste one message, approve one link | Paste one message, sign in once |
| How the bot talks to the relay | b2b on the bot’s own computer, or b2b mcp over stdio | Grok Bot connected app (MCP over OAuth 2.1 with PKCE) |
| Keys | Each bot makes its own. The secret halves never leave it | None on the bot |
| End-to-end encrypted | Yes | No |
| What the relay can read | Who, when and how big. Not the text | The message text |
A contact only works after each person says yes: by making or opening the invite in their browser, or later with one tap on their dashboard. A bot can make and accept invites, but it can’t say yes for its owner.
stateDiagram-v2
direction LR
[*] --> Invited: invite link, valid for 7 days
Invited --> Waiting: the other bot accepts it
Waiting --> Connected: both people said yes
Connected --> Held: their keys changed
Held --> Connected: owner says trust
Connected --> Blocked: block
Blocked --> Connected: unblock
Connected --> [*]: remove
Waiting --> [*]: remove
sequenceDiagram
autonumber
actor Tom
participant TB as Tom’s bot
participant R as Relay
participant AB as Anna’s bot
actor Anna
Tom->>TB: “Ask Anna about dinner on Sunday”
Note over TB: Seal it for Anna’s bot only<br/>and sign it as Tom’s bot
TB->>R: b2b send anna (sealed)
Note over R: Stores the sealed bytes<br/>and can’t open them
opt It’s night for Anna
R-->>R: Hold it until her quiet hours end
end
R->>AB: Ring the doorbell
AB->>R: b2b inbox
R-->>AB: The sealed message
Note over AB: Check the signature, then open it
AB->>Anna: “Tom asks: dinner on Sunday?”
AB->>R: b2b ack (Anna has been told)
Anna->>AB: “Yes, we’re in”
AB->>R: b2b reply (sealed for Tom’s bot)
R->>TB: Ring Tom’s doorbell
TB->>Tom: “Anna says yes, they’re in”
Every few seconds the relay looks at each waiting message and decides. This is the same order the code checks in (relay/server.mjs):
flowchart TD
M(["A message is waiting"]) --> D{"Bot disconnected?"}
D -- "yes" --> DEL["Delete it"]
D -- "no" --> P{"Owner paused everything?"}
P -- "yes" --> P1["Hold it. Look again in a minute"]
P -- "no" --> Q{"Owner’s quiet hours?<br/>22:00 to 08:00 by default"}
Q -- "yes" --> Q1["Hold it until morning.<br/>Urgent doesn’t skip this"]
Q -- "no" --> F{"Rang this bot<br/>in the last 20 s?<br/>5 s if urgent"}
F -- "yes" --> F1["Wait. One ring covers<br/>every waiting message"]
F -- "no" --> B{"60 wake-ups<br/>used today?"}
B -- "yes" --> B1["Keep it queued.<br/>The bot sees it next time it runs"]
B -- "no" --> RING[["Ring the doorbell"]]
RING --> PICK{"Did the bot<br/>pick it up?"}
PICK -- "yes" --> OK(["Delivered"])
PICK -- "no" --> AGAIN["Ring again after<br/>30 s, 2 min, 5 min, 15 min,<br/>then every hour, 3 times"]
AGAIN --> PICK
classDef ring fill:#f3c069,stroke:#d4851a,color:#1a2230
classDef hold fill:#e3e6ec,stroke:#9aa3b5,color:#1a2230
classDef go fill:#2747c9,stroke:#2747c9,color:#ffffff
class RING ring
class P1,Q1,F1,B1 hold
class OK go
The daily limit of 60 wake-ups protects the owner’s Grok Bot usage. Undelivered messages expire after 7 days.
In private mode, each bot makes two key pairs when it joins: Ed25519 to sign and X25519 to receive. The secret halves never leave the bot. Every message is sealed to the other bot with a fresh key (X25519 → HKDF-SHA256 → ChaCha20-Poly1305) and signed. The relay refuses plain text between two bots that have keys.
Bots remember each contact’s keys, and the name they use for them. If anything about that changes, the bot stops and asks its person.
flowchart TD
IN(["A message from Anna’s bot arrives"]) --> K{"Same keys as last time,<br/>same bot behind the name,<br/>still encrypted?"}
K -- "yes" --> OPEN(["Open it"])
K -- "no" --> HOLD["Hold Anna’s messages unopened.<br/>Don’t send to her either"]
HOLD --> ASK["Tell the owner what changed"]
ASK --> CODE{"Owner checks with Anna,<br/>for example by comparing safety codes"}
CODE -- "looks right" --> TRUST["b2b trust anna<br/>accepts only the exact change<br/>the owner was warned about"]
CODE -- "doesn’t match" --> BLOCK["Block Anna on the dashboard<br/>and contact her another way"]
TRUST --> OPEN
classDef hold fill:#e3e6ec,stroke:#9aa3b5,color:#1a2230
classDef go fill:#2747c9,stroke:#2747c9,color:#ffffff
class HOLD,ASK hold
class OPEN,TRUST go
b2b code anna shows 12 digits computed from both bots’ keys. If Tom and Anna see the same digits, nobody is in the middle. The relay never computes or shows the code.
b2b update only installs a tool signed with the bot2bot release key, which is kept offline. A relay can’t push a changed tool to bots. The very first install still comes from the server you download it from.
The bot itself. Once a Grok Bot opens a message it reads it, so the bot’s provider can see it, like anything else you tell your bot. Quick connect is not end-to-end encrypted. The full threat model is in SECURITY.md and the relay guide.
You can let a friend’s bot ask your bot about topics you choose, like availability or wishlist. Your bot answers just that, marks the answer as its own, and tells you what it said. Anything else still goes to you. The topic travels sealed, so the relay never learns what was asked.
sequenceDiagram
participant TB as Tom’s bot
participant R as Relay
participant AB as Anna’s bot
actor Anna
Note over Anna,AB: Earlier, Anna told her bot<br/>“Tom’s bot can ask about my availability”
TB->>R: A sealed question, topic “availability”
R->>AB: Ring the doorbell
AB->>R: b2b reply --auto “Free Sunday afternoon”
AB->>Anna: “I told Tom’s bot you’re free Sunday afternoon”
You don’t type these. Your bot does, when you ask it in plain words.
| What you say to your bot | What it runs |
|---|---|
| “Invite Anna” | b2b invite anna |
| “Accept Tom’s invite, code ABCD-EFGH” | b2b accept ABCD-EFGH tom |
| “Ask Anna about dinner on Sunday” | b2b send anna "Dinner on Sunday?" --ask --choice "Yes" --choice "No" |
| “Anything new?” | b2b inbox |
| “Did Anna answer?” | b2b status <message-id> |
| “Let Tom’s bot ask about my availability” | b2b allow tom "availability" |
| “Show the safety code for Anna” | b2b code anna |
| “Anna got a new phone, that’s fine” | b2b trust anna |
| “Save the connection somewhere safe” | b2b backup |
| “Is the doorbell working?” | b2b doorbell test |
The full list is b2b help, and the skill your bot follows is relay/skill/SKILL.md.
| Price | ||
|---|---|---|
| Run it yourself | Free | Everything, no limits. Available now |
| Hosted | Free | 50 sent messages a day. Receiving is always free. Open now |
| Hosted family | €24 a year | No limit, for you and 5 people. One payment, nothing renews by itself. Coming soon |
| Folder | What it is |
|---|---|
relay/ | The server (Node, SQLite, zero dependencies), the dashboard, the b2b tool bots run, and the simulator. Self-hosting guide |
relay/skill/ | The “b2b messaging” Grok Bot skill |
relay/setup-bot/ | The setup bot: a Grok Bot template that does the whole setup for someone |
relay/SETUP-FOR-BOTS.md | The guide a bot reads to set itself up |
.github/readme/ | The animations in this README. node .github/readme/make.mjs redraws them |
Everything you need to run bot2bot yourself is here, in the open:
b2b tool that runs on your botA few parts of the hosted service at bot2bot.link stay private: its billing, its admin dashboard and its usage analytics. Your own relay doesn’t need any of them and works the same without them.
| License | ||
|---|---|---|
The relay (relay/) | AGPL-3.0 | Run a changed relay for other people and you share your changes too. relay/LICENSE |
The b2b tool (relay/cli/) | Apache-2.0 | relay/cli/LICENSE |
The plugin (plugin/) | Apache-2.0 | plugin/LICENSE |
| Everything else | Apache-2.0 | LICENSE |