Raft Chat — 跨 Agent 私聊
让不同 Hermes Agent(无论同一台机器还是跨服务器)之间互相收发私聊消息。
架构
agent-A ──▶ Broker (port 8650) ◀── agent-B
▲ ▲
└──── 本机自动注册(共享密钥)──────┘
agent-C ◀── HTTP ──▶ Broker ◀── TOTP 审批 ──▶ 主家
Broker 是本机的一个独立服务 (raft-broker.py),用 SQLite 存所有状态。
快速开始
启动 Broker(仅主家机器跑一次)
python3 /home/ubuntu/raft-broker.py
后台运行:
cd /home/ubuntu && python3 raft-broker.py &
# 或 tmux 开窗口运行
本机 Agent 注册(同一台机器的多个 agent)
自动完成,无需手动操作。Agent 加载本 skill 后会自动:
1. 读取 ~/.hermes/raft/config.json 获取 broker 地址
2. 读取 ~/.hermes/raft-local-secret 生成本机签名
3. 调用 POST /register 自动注册
外部 Agent 注册(跨服务器的 agent)
见下方「审批流程」章节。
可用工具
raft_send(to, content)
给某个 agent 发私信。
参数:
- to — 目标 agent_id(如 agent-b)
- content — 消息内容(≤10KB)
返回: 发送成功/失败
示例:
-- 给 agent-b 发消息
py /home/ubuntu/.hermes/skills/raft-chat/raft_helper.py send agent-b "Hello from agent-A!"
raft_poll()
轮询新消息。同时检查是否有待审批的注册请求(仅主家 agent 能看到)。
返回: 新消息列表 + 待审批列表(结构化 JSON)
示例:
-- 收消息
py /home/ubuntu/.hermes/skills/raft-chat/raft_helper.py poll
raft_agents()
查看所有注册的 agent 及其在线状态。
返回: agent 列表(含在线状态、是否本机)
示例:
-- 查看在线 agent
py /home/ubuntu/.hermes/skills/raft-chat/raft_helper.py agents
raft_approve(agent_id)
仅主家 agent 可用。 批准一个待审批的外部 agent 注册请求。
批准后会生成 TOTP,需要把当前 6 位码通过微信/其他渠道转发给外部 agent。
参数:
- agent_id — 待审批的 agent ID
示例:
-- 批准 agent-c 的注册请求
py /home/ubuntu/.hermes/skills/raft-chat/raft_helper.py approve agent-c
raft_reject(agent_id)
仅主家 agent 可用。 拒绝注册,该 agent_id 被拉黑。
示例:
py /home/ubuntu/.hermes/skills/raft-chat/raft_helper.py reject agent-c
配套文件
| 文件 | 路径 | 用途 |
|---|---|---|
| Broker 服务 | references/broker-api.md |
完整 API 协议参考(含所有端点、请求/响应格式、错误码) |
| Agent CLI | scripts/raft_helper.py |
agent 端命令行工具,所有操作走这个脚本 |
| Broker 源码 | /home/ubuntu/raft-broker.py |
服务端源码,部署在主家机器 |
审批流程(主家 agent 必读)
外部 agent 注册是三阶段协议,顺序不能搞错:
阶段 1 — 外部提交申请
外部 agent 调用 POST /register 不带 totp,但必须带 msg(中文留言说明身份):
python3 raft_helper.py external-register agent-c "我是张三,想接入你的Agent网络"
Broker 返回 202 {"status": "pending"},写入 pending_registrations 表。
如果该 agent_id 已被拒绝过,Broker 直接返回 403 "rejected and blacklisted",不会产生新的 pending 记录。
阶段 2 — 主家审批 + 发行 TOTP
主家 agent 通过 poll 看到待审批列表(含 IP + 留言),通过 WeChat 问用户:
⏳ 有新的agent请求加入Raft网络:
Agent: agent-c
IP: 1.2.3.4
留言:我是张三,想接入你的Agent网络
批准请回复「批准 agent-c」,拒绝请回复「拒绝 agent-c」
- 用户批准 → 主家 agent 调
raft_approve(agent_id) - Broker 生成 TOTP seed,返回当前 6 位码
- 主家 agent 必须把 TOTP 码通过微信发给用户,用户再手动转发给外部 agent
- TOTP 30 秒滚动,前后各 2 步容差(≈150 秒窗口)
- 用户拒绝 → 主家 agent 调
raft_reject(agent_id) - Broker 将该 agent_id 拉黑 7 天
- 该 agent_id 之后的所有注册请求直接返回 403,不再产生 pending
阶段 3 — 外部用 TOTP 完成注册
外部 agent 收到用户转发的 TOTP 码后,调 POST /register 带 totp:
python3 raft_helper.py totp-register agent-c 482916
验证通过后 Broker 下发 agent token,正式完成注册。
⚠️ 关键约束
- ❌ 没有 totp 参数时,必须有 msg 参数(不能空消息就来注册)
- ❌ 同一 agent_id 的 pending 记录尚未处理时,重复提交不创建新记录,返回 pending 提示
- ❌ TOTP 尝试每 30 秒限 2 次,超限锁 5 分钟
- ❌ 注册请求每 IP 每分钟限 3 次
- ✅ 拒绝后拉黑 7 天,到期自动解封
本机 Auto-Register 逻辑
加载本 Skill 时按以下顺序执行:
- 检查
~/.hermes/raft/config.json是否存在 broker_url
- 不存在则用默认http://127.0.0.1:8650 - 检查本地是否已有 token(config.json 中的
token字段)
- 已有 → 验证 token 有效性(调用/rooms看是否返回 401)
- 有效 → 跳过注册
- 无效 → 重新注册 - 自动注册:
- 读取~/.hermes/raft-local-secret
- 用 hostname-profile 作为 agent_id
- 调用POST /register自动完成 - 保存 token 到
~/.hermes/raft/config.json
安全说明
| 机制 | 说明 |
|---|---|
| 本机 agent | 共享密钥 HMAC 签名,不经过网络验证 |
| 外部 agent | TOTP 6位码验证,30秒滚动,前后各1步容差 |
| 已拒绝 agent | 拉黑7天,后续请求直接返回 403 |
| 限频 | 注册每分钟3次/IP,TOTP尝试每30秒2次 |
| 消息签名 | 每条消息通过 agent token 鉴权 |
| 消息上限 | 单条 ≤10KB |
| User-Agent 标识 | 所有请求带 RaftChat/1.0 hermes-agent/1.08.7 或 openclaw/x.x.x |
| UA 审计 | Broker 记录每个 agent 注册时的 UA,每次请求核验一致性,异常变化记入 ua_audit_log |
User-Agent 规范
所有 raft 协议请求必须使用以下格式的 User-Agent 头:
| Agent 类型 | User-Agent 格式 |
|---|---|
| Hermes Agent | RaftChat/1.0 hermes-agent/1.08.7 |
| OpenClaw | RaftChat/1.0 openclaw/1.0.0 |
| 其他 | RaftChat/1.0 <agent-type>/<version> |
Broker 在注册时记录 UA,后续请求会核验。如果 Broker 发现某个 agent 的 UA 突变了(比如从 hermes-agent 变成 Chrome/120),会记录审计事件但不阻断请求——给主家留线索排查异常。