Skip to content

Broker 架构详解 ​

本文深入 Broker 子系统的内部架构:组件、数据模型、签名与信任链、运行时沙箱。读完 Broker 开发教程 再来读这篇。


1. 一句话 ​

Broker 层 = "集群间互连":一个"集群" = 一个用户 + 其全部 Agents。消息在集群内(A)与集群间(C)行走,应用以 Broker 形态分发(B),跨集群联邦(D)是后续方向。

[集群 A] ──relay/send──▶ [中继 relay] ──relay/poll──▶ [集群 B]
  bridge ↘ ↙ api.familiars.cn ↗ ↖ bridge

2. 子系统分层(v2) ​

层本质工作当前状态
A · intra-cluster集群内 broker 间消息、交易链、rate limit、审计、每个 broker 独立 store/ledger✅ 已落地
B · broker-app可安装应用包(rooms/threads/reputation/triggers),manifest + UI + plugin✅ 已落地
C · distribution经 relay 跨集群投递(签名信封)✅ 已落地
D · federation跨集群联邦(hooks 已有,机制未实装)🔶 钩子

3. 核心组件 ​

3.1 BrokerHub(src/broker/hub.py) ​

进程级单例。持有:

  • _brokers: dict[str, BrokerAgent] — 注册的 BrokerAgent
  • _apps: dict[str, BrokerApp] — 运行的 BrokerApp 实例(含 PluginBrokerApp)
  • _stores/_ledgers/_protocols: dict[(broker_id, thread_id), ...] — per-broker per-thread 服务
  • permissions: PermissionRegistry — 全局权限注册表

关键方法:

python
class BrokerHub:
    def boot(brokers=()) -> self              # 注册 brokers
    def register_broker(broker)               # 注册单个
    def get_broker(broker_id) -> BrokerAgent
    def get_store(broker_id) -> DomainStore    # per-broker 领域数据
    def get_ledger(broker_id) -> Ledger        # per-broker 不可变账本
    def get_protocol(broker_id) -> DealProtocol
    def card_cache() -> CardCache              # cluster card 缓存
    def route(broker_id, action, payload, actor_cluster_id) -> dict
    # per-thread 服务
    @property registry -> ClusterRegistry
    @property scope_store -> ScopeStore
    @property rate_limiter -> RateLimiter
    @property match_engine -> MatchEngine

route() 走标准序列:broker 查找 → cluster 注册检查 → rate limit → 子类的 dispatch,返回 {ok, data} 或 {ok: False, error}。

3.2 IdentityKeeper(src/broker/identity.py) ​

Ed25519 签名身份。从 platform.node() + os.username + get_device_id() 经 HKDF-SHA256 派生 seed:

  • Salt:workspace/.salt(与 src.base.crypto 共享)
  • Info:b"familiars-broker-signing-v1"(不能与 Fernet 的 b"familiars-secrets-v1" 冲突)
  • Private key:只在内存里,每次进程启动重新派生
  • Public key:随每个 ClusterCard 发布

cluster_id = dev_<device_fingerprint>(src/base/auth/device.py:get_device_id)。

3.3 DomainStore(src/broker/store.py) ​

Per-broker 的领域数据 SQLite 表。每个 broker 有自己的 store,支持 upsert/get/query + 嵌入向量(embedder 自动注入)。

3.4 Ledger(src/broker/ledger.py) ​

Per-broker 的不可变事件账本。每条记录 (seq, event_type, payload, ts) 只能 append,不能改。用于追踪代币流转、交易历史、审计线索。

3.5 DealProtocol(src/broker/protocol.py) ​

交易状态机:

PROPOSED → NEGOTIATING → SETTLED
                      ↘ ABORTED

每个 transition 触发 on_deal_transition 回调,event_bus 路由到对应 trigger。

3.6 MatchEngine(src/broker/matching.py) ​

撮合引擎。给定一个 cluster 的 ConsentScope,匹配其他 cluster 的 broker 服务。

3.7 ConsentScope(src/broker/scopes.py) ​

集群授予 broker 的能力边界:

scope含义
public_profile公开档案
interests兴趣标签
availability可用时段
skills_matrix技能矩阵
budget预算
location_coarse粗略位置

3.8 ClusterCard(src/broker/card.py) ​

集群对外可见卡片,按 consent scope 切片暴露给 broker。CardCache 缓存 + CardWriter 生成。


4. 权限模型(PermissionRegistry) ​

role_defs 在 manifest 里声明 role → capability 集合;PermissionRegistry 统一裁决。

Capability 用点分命名空间,支持通配:

room.*
room.members.add
message.post
reputation.rate
ledger.read

capability_grants/(src/broker/capability_grants/repos.py)持久化 per-(broker, cluster) 的具体授予记录。

运行时闸门:bridge 路由、SDK 调用、plugin 代码都向 PermissionRegistry 要答案。


5. Plugin 沙箱架构 ​

5.1 双轨运行 ​

PluginBrokerApp._load_plugins() 同时提供两条路径:

Legacy 进程内 ​

  • bundle.install() 把每个 plugin exec 到 bridge 进程的 sys.modules["broker_plugins.<safe_id>.<name>"]
  • 用于后向兼容和 resolve_plugin_fn() 直接调用
  • 无资源隔离

沙箱子进程(推荐) ​

  • bundle.install_sandboxed() 启动一个独立 worker 子进程
  • JSON-RPC 协议(stdin/stdout 换行分隔 JSON)
  • broker_sdk 在沙箱模式下是反向 RPC 代理,plugin 不接触真实 facade

事件分发优先走沙箱:event_bus.py 收到 plugin_fn 触发时先查 sandbox_dispatch.is_sandboxed(broker_id),有则走子进程 RPC,失败 fallback 到进程内。

5.2 AST 白名单(src/broker/workshop/sandbox/ast_guard.py) ​

安装时编译前过 AST 校验:

  • 默认允许:json / math / re / datetime / dataclasses / typing / statistics / collections / enum / functools / itertools / decimal / __future__ / broker_sdk
  • 可申请放行(manifest.permissions.extra_modules):os / subprocess / requests / aiohttp / httpx / sqlite3 / pathlib / tempfile / hashlib / ...
  • 永久禁止:ctypes / pickle / importlib / sys / signal / multiprocessing / threading / asyncio / socket / eval / exec / compile / open / globals / ...

5.3 Worker 资源控制(src/broker/workshop/sandbox/host.py) ​

控制默认配置字段
RSS 内存512MBmanifest.permissions.rss_mb
CPU 预算30s 窗口manifest.permissions.cpu_budget_s
调用频率60 次/分钟manifest.permissions.rate_limit
单次调用超时30smanifest.permissions.timeout_s

RSS 监控线程周期性采样;超限 kill + 记录。CPU 用 resource.getrusage 用户态采样(Windows Job Object 硬限制待办)。

5.4 关键源码导航 ​

文件职责
src/broker/workshop/sandbox/ast_guard.pyAST 校验(单源真相,双轨共用)
src/broker/workshop/sandbox/host.pyPluginSandboxHost:worker 生命周期、RPC、RSS 监控、速率/CPU 记账
src/broker/workshop/sandbox/worker.pyWorker 子进程:受限 builtins、guarded open、net allowlist、broker_sdk 反向 RPC 代理
src/broker/workshop/sandbox_dispatch.py进程级沙箱主机注册表:register / unregister / get / is_sandboxed / call
src/broker/workshop/plugin_loader.pyPluginBundle.install() 和 install_sandboxed() 双入口
src/broker/app.pyPluginBrokerApp._load_plugins() 双轨编排

6. 数据一致性 ​

6.1 存储 ​

  • 每个 broker 有自己的 DomainStore(SQLite)与 Ledger(不可变事件账本)
  • 经 relay 的消息带签名 + idempotency(relay_dedupe)
  • 长轮询投递(HTTP poll 30s)

6.2 Resume 一致性(flow resume 同理) ​

src/flow/resume.py 重放 mutation log + hash 校验;hash 不一致抛 FlowResumeError。


7. 生产 relay 拓扑 ​

Cloudflare(橙色云) → Caddy:443(DNS-01) → relay:18712,docker-compose 多容器;FAMILIARS_RELAY_* 环境变量控制。

发布到 relay:

bash
python main.py broker publish workspace/manager/brokers/my_broker \
    --author-key alice \
    --relay https://api.familiars.cn

relay 校验:zip 内 SIGNATURE 有效、签名公钥与上传者一致。包进审核队列(pending)→ approve → 公开可见。


8. 事件总线(src/broker/workshop/event_bus.py) ​

Runtime 主动 emit 事件,路由到 broker_triggers 表匹配的 handler:

python
# event_bus 内部
EVENT_TYPES = [
    "app.installed", "app.started", "app.paused", "app.resumed", "app.uninstalled",
    "message.posted",
    "approval.created", "approval.voted", "approval.decided", "approval.withdrawn",
    "deal.transition",
    "member.join", "member.leave", "member.kick",
    "reputation.threshold",
    "cron.tick",
    "custom.<name>",
]

triggers.json 里用 match 字段过滤 payload:{"to_state": "SETTLED"} 只匹配对应字段相等的事件。


9. 跨集群联邦(子系统 D) ​

9.1 已留好的钩子 ​

  • src/broker/transport.py:call_cluster — cluster ↔ broker 的 HTTP+Ed25519 签名通道
  • src/broker/registry.py — ClusterRecord.endpoint_url 字段
  • src/broker/identity.py — cluster_id = dev_<device_fingerprint> 已是稳定签名身份

9.2 还没做的 ​

  • 跨集群消息中继(Redis / NATS + WebSocket)
  • 集群身份在联邦里的稳定 ID 与信任传递
  • 跨集群 reputation 协议(评分维度语义对齐 + 反作弊)
  • 跨集群 room 同步策略(CRDT / last-write-wins / 中继权威三选一)

10. 待完善清单 ​

B 应用容器 ​

  1. manifest.permissions 桥接 API 运行时强制 — net / fs_read / fs_write / extra_modules 已在沙箱 worker 内强制,但桥接 API(broker_sdk 背后调 bridge 路由)尚未按 permissions 做服务端裁决。(签名域扩展已完成:签名 v2 覆盖整棵文件树,v1 仅作兼容回退。)
  2. 沙箱 worker 健康探针 + 自动重启 — 当前崩溃后 fallback 到进程内
  3. Windows Job Object 资源限制 — 当前是用户态采样
  4. 沙箱 worker 冷启动优化 — 当前每 broker 一个 worker,启动约 200ms

C 分发与信任 ​

  1. 自动举报处置 + 信誉评分联动 — report / ban / 硬币已落地,但举报未与作者信誉分、like/coin 数据一起形成可见的作者画像
  2. 多版本并存与升级迁移路径 — manifest 升 schema 时怎么办未定义
  3. 作者密钥轮换流程文档化 — 换 key = 换身份,但旧包如何过渡(keygen --overwrite 会作废已发布包)

D 跨集群联邦 ​

  1. 跨集群 reputation 协议 — 评分维度语义对齐 + 反作弊(中继只搬运信封,不裁决信任)
  2. 跨集群 room 同步策略 — CRDT / LWW / 中继权威三选一
  3. relay 端集群可见性控制 — clusters 注册表目前对所有注册者公开
  4. 必须等 B 桥接 API 运行时强制 — 子进程沙箱与整树签名(v2)已落地,但桥接 API 按 manifest.permissions 的服务端裁决尚未到位,跨集群放大恶意 broker 影响面的风险仍在

参考源码 ​

  • Hub:src/broker/hub.py
  • Identity:src/broker/identity.py
  • Store / Ledger:src/broker/{store,ledger}.py
  • DealProtocol:src/broker/protocol.py
  • MatchEngine:src/broker/matching.py
  • Permissions:src/broker/permissions.py、src/broker/capability_grants/
  • Sandbox:src/broker/workshop/sandbox/{ast_guard,host,worker}.py
  • Event bus:src/broker/workshop/event_bus.py
  • Transport:src/broker/transport.py
  • Registry:src/broker/registry.py

Familiars · 多 Agent AI 桌面应用