Broker 应用包开发详解
本文是 Broker 子系统的完整开发参考。Broker 是 Familiars 的"可分享应用包":一个
.broker.zip包含 manifest + UI 资产 + plugin + trigger + recipe,可在市场安装、跨用户复用。
1. 概念模型
1.1 Broker 是什么
Broker = Familiars 的集群互联层的应用容器。
集群(cluster) = 一个用户 + 该用户名下的全部 agent。它是 Familiars 最小的信任单元:集群内部所有 agent 共享一份记忆与一套权限,对外作为单一签名身份出现(cluster_id = dev_<device_fingerprint>,由 src/broker/identity.py 派生)。
Broker 层四个子系统:
| 子系统 | 本质工作 | 当前状态 |
|---|---|---|
| A. 集群内编排 Intra-cluster | 一个集群内部多 agent 结构化协同:rooms / threads / deals / reputation | ✅ 已落地 |
| B. 应用容器 App container | 第三方代码作为可插拔能力挂进任意集群 | ✅ 已落地(AST 白名单 + 子进程沙箱,双轨运行) |
| C. 分发与信任 Distribution & Trust | 作者签名 → 静态站/relay → 用户安装;Ed25519 + sha256 + 作者公钥交叉核对 | ✅ 已落地(Layer 1 MVP) |
| D. 跨集群联邦 Cross-cluster Federation | 不同用户的集群之间传消息、可信路由 | 🔶 钩子已留,中继未实现 |
1.2 包结构
一个 .broker.zip 包含:
my_broker.broker.zip
├── manifest.json 必需:broker 的一切描述
├── SIGNATURE 必需(marketplace 强制):格式 <pub_key_b64>\n<sig_b64>\n
├── ui/ 可选:iframe UI 目录
│ ├── index.html manifest.ui.entry 指向的入口
│ └── ...
├── plugins/ 可选:*.py 通过 AST 白名单 + 子进程沙箱加载
├── triggers.json 可选:事件 → plugin/recipe 分发表
├── recipes/ 可选:recipe 归档
├── cover.png 可选:桌面图标
└── README.md 可选打包器(broker pack)拒绝 backslash 路径、.. 转义、绝对路径、奇怪字符文件名。
2. manifest.json 完整字段
{
"broker_id": "my_broker",
"name": "My Broker",
"version": "0.1.0",
"schema_version": 1,
"author_pubkey": "<base64 公钥>",
"description": "...",
"domain": "collaboration",
"python_api_version": 1,
"capabilities_required": ["guild", "ledger", "recipe_call", "scheduler"],
"role_defs": {
"owner": {"caps": ["*"]},
"approver": {"caps": ["task.approve", "task.verdict", "settle.trigger"]},
"issuer": {"caps": ["task.publish", "task.read"]},
"developer": {"caps": ["task.accept", "task.deliver"]},
"audience": {"caps": ["task.read"]}
},
"role_notes": {"sub_owner": "..."},
"reputation_dimensions": [
{"key": "delivery", "label": "交付质量", "min": 0, "max": 10},
{"key": "punctuality", "label": "准时率", "min": 0, "max": 10}
],
"ui": {
"entry": "index.html",
"title": "My Broker",
"width": 1000,
"height": 680,
"single_instance": true
},
"permissions": {
"network": false,
"fs_read": false,
"fs_write": false,
"extra_modules": ["requests"],
"rss_mb": 512,
"cpu_budget_s": 30,
"rate_limit": 60,
"timeout_s": 30
},
"on_install": [
{"kind": "cron", "cron_expr": "0 * * * *", "action_kind": "plugin_fn", "action_ref": "plugins.mymod:tick"}
],
"on_uninstall": []
}2.1 必需字段
| 字段 | 类型 | 说明 |
|---|---|---|
broker_id | string | 全局唯一,== 目录名 == zip 内 id |
name | string | 显示名 |
version | string | 推荐 semver |
schema_version | int | 当前 = 1 |
2.2 推荐字段
| 字段 | 说明 |
|---|---|
description | 描述 |
domain | 业务域 |
author_pubkey | 你的公钥(marketplace 跟包内 SIGNATURE 交叉核对) |
ui.entry | iframe UI 入口(相对 ui/ 的 .html) |
capabilities_required | 向集群声明要哪些本地 API |
role_defs | role → capability 集合 |
reputation_dimensions | 信誉维度 |
2.3 capabilities_required 常见值
| 能力 | 含义 |
|---|---|
guild | 公会/房间/帖子/任务库 |
ledger | 代币账本(发行/转账/结算) |
recipe_call | 调用本地 Recipe |
scheduler | 定时触发 |
rooms.read / rooms.write | 会话读/写 |
messages.post | 会话消息发布 |
tasks.write / tasks.approve | 任务发布/审批 |
settle.trigger | 触发结算 |
2.4 permissions(plugin 沙箱权限)
| 字段 | 默认 | 含义 |
|---|---|---|
network | false | HTTP 访问外部服务 |
fs_read | false | 读文件系统 |
fs_write | false | 写文件系统 |
extra_modules | [] | 申请放行模块(见 §6) |
rss_mb | 512 | RSS 内存上限 |
cpu_budget_s | 30 | 30s 窗口 CPU 预算 |
rate_limit | 60 | 调用次数/分钟 |
timeout_s | 30 | 单次调用超时 |
3. UI 开发
3.1 单 HTML 骨架(starter 风格)
ui/index.html 作为入口,通过 <script src="../sdk.js"> 拿 window.familiars。bridge 的 /api/broker_app/<id>/sdk.js 提供该 shim。
<!DOCTYPE html>
<html>
<head><script src="../sdk.js"></script></head>
<body>
<h1 id="title"></h1>
<button onclick="load()">Load rooms</button>
<pre id="out"></pre>
<script>
const api = window.familiars;
document.getElementById('title').textContent = 'Hello, ' + api.broker_id;
async function load() {
const res = await api.rooms.list();
document.getElementById('out').textContent = JSON.stringify(res, null, 2);
}
</script>
</body>
</html>完整 SDK API 见 workspace/manager/brokers/SDK.md。
3.2 Vue/Vite 工程(进阶)
在 ui/ 里放一个完整工程(src/App.vue、vite.config.ts、package.json),把构建产物 dist/ 打进 .broker.zip(entry 指向 dist/index.html)。
参考:workspace/manager/brokers/example_broker/(ui_mode: "vue"、entry: "./dist/index.html")。
发布前先在 ui/ 跑 npm install && npm run build 生成 dist/。
4. Plugin 开发
4.1 双轨沙箱架构
plugins/*.py 安装时经过两道关卡:
- AST 白名单校验(安装时,编译前)— 拒绝禁止的 import、属性链、
exec/eval/open等危险名称; - 运行时沙箱(
PluginBrokerApp.on_start时)— 双轨运行:- Legacy 进程内路径:
bundle.install()exec 到 bridge 进程的sys.modules["broker_plugins.<safe_id>.<name>"]; - 沙箱子进程路径:
bundle.install_sandboxed()为每个 broker 启动独立 worker 子进程,通过 JSON-RPC(stdin/stdout 换行分隔 JSON)通信。
- Legacy 进程内路径:
事件分发优先走沙箱:event_bus.py 收到 plugin_fn 触发时先查 sandbox_dispatch.is_sandboxed(broker_id),有则走子进程 RPC,失败时 fallback 到进程内。
4.2 Plugin 函数签名
# plugins/greeter.py
from broker_sdk import store
def on_message_posted(payload: dict) -> None:
store.upsert(
f"seen/{payload['message_id']}",
title="seen",
meta={"cluster_id": payload.get("cluster_id")}
)- 接收单个位置参数的 payload 字典;
- facade(
store/rooms/ledger…)从broker_sdk导入;沙箱模式下是反向 RPC 代理,进程内是真实 SDK; - 不要给函数加
sdk参数(旧版已废弃)。
4.3 triggers.json
[
{
"event": "message.posted",
"action_kind": "plugin_fn",
"action_ref": "plugins.greeter:on_message_posted"
},
{
"event": "approval.decided",
"action_kind": "plugin_fn",
"action_ref": "plugins.greeter:on_approval_decided"
}
]action_kind 可选值:plugin_fn | recipe_call | spawn_agent | ...
match 字段可过滤 payload:{"to_state": "SETTLED"} 只匹配字段相等的事件。
4.4 完整事件目录
| Event | Payload keys |
|---|---|
app.installed | broker_id, installed_from |
app.started / paused / resumed / uninstalled | broker_id |
message.posted | broker_id, room_id, thread_id?, cluster_id, kind, message_id |
approval.created / voted / decided / withdrawn | approval_id, ... |
deal.transition | deal_id, from_state, to_state, actor, evidence |
member.join / leave / kick | room_id, cluster_id, ... |
reputation.threshold | subject, dimension, from, to, delta |
cron.tick | cron_expr, ts |
custom.<name> | plugin-defined |
5. 签名与安全
5.1 三条强制校验
用户端 install_from_url 依次校验,不过全拒:
sha256:下载内容哈希与 index 记录严格一致;SIGNATURE:zip 内<pubkey>\n<sig>\n,用 pubkey 验证sha256(manifest.json)签名;author_pubkey:签名里的 pubkey 必须与 index 声明一致。
5.2 作者签名密钥
- 位置:
~/.familiars/authors/<name>.key(Windows:%USERPROFILE%\.familiars\authors\<name>.key) - 格式:单行 base64 编码的 32 字节 Ed25519 seed
- 跨机器同步:拷
.key文件即可
python main.py broker keygen --name <name> # 生成
python main.py broker keys-list # 列出
python main.py broker keygen --name <name> --overwrite # 换密钥5.3 当前签名域的简化(已知裂缝)
当前只签 manifest.json,ui/ / plugins/ / triggers.json 可被改而不失效签名。修改 manifest 后必须 re-sign。B 子系统的 backlog 项目。
6. AST 白名单
6.1 默认允许
json / math / re / datetime / dataclasses / typing / statistics / collections / collections.abc / enum / functools / itertools / decimal / __future__ / broker_sdk
6.2 可申请放行(extra_modules)
os / subprocess / requests / aiohttp / httpx / sqlite3 / pathlib / tempfile / hashlib / hmac / base64 / binascii / uuid / secrets / shutil / glob / fnmatch / textwrap / io / copy / pprint / urllib / xml / csv / string / bisect / heapq / operator / itertools / random / fractions / warnings / contextlib / abc / types / inspect / argparse
6.3 永久禁止
ctypes / pickle / importlib / inspect / code / codeop / pdb / trace / tracemalloc / sys / signal / multiprocessing / threading / asyncio / socket / select / asyncore / asynchat / ssl / mmap / platform / __import__ / eval / exec / compile / open / globals / locals / vars / getattr / setattr / delattr / breakpoint / input / exit / quit
7. 生命周期钩子
manifest.on_install / on_uninstall 是一组 {kind, ...} 对象:
| kind | 含义 |
|---|---|
cron | {"kind": "cron", "cron_expr": "0 * * * *", "action_kind": "plugin_fn", "action_ref": "plugins.mymod:tick"} |
spawn_agent | {"kind": "spawn_agent", "template": "..."} |
bind_recipe | {"kind": "bind_recipe", ...} |
run_script | (未落地,MVP 不推荐用) |
详见 src/broker/workshop/packaging.py:_validate_lifecycle_hooks。
8. REST API
8.1 实例管理(/api/broker_workshop)
| Method | Path | 说明 |
|---|---|---|
| GET | /instances | 列已装 broker |
| GET | /instances/{broker_id} | 取一个 |
| POST | /instances/import | 从目录导入(本地开发) |
| POST | /instances/install_from_url | 从 URL 安装(marketplace 强制三校验) |
| DELETE | /instances/{broker_id} | 卸载(store 来源拒) |
| POST | /instances/consent | 安装同意(能力审批) |
| GET | /instances/consents/{user_id} | 列用户同意 |
| GET | /instances/updates | 检查更新 |
8.2 Marketplace
| Method | Path | 说明 |
|---|---|---|
| GET | /marketplace/index | 取市场索引 |
| GET | /marketplace/recipes/index | 列市场 recipe |
| GET | /marketplace/flows/index | 列市场 flow |
| POST | /marketplace/{kind}/like | 点赞 |
| POST | /marketplace/{kind}/coin | 打赏 |
| POST | /marketplace/coin/balance | 查余额 |
| POST | /marketplace/{kind}/view | 记录浏览 |
| POST | /marketplace/report | 举报 |
| POST | /marketplace/cover/upload | 上传封面 |
| POST | /marketplace/cover/generate | 生成封面 |
8.3 能力审批
| Method | Path | 说明 |
|---|---|---|
| GET | /capabilities/registry | 能力注册表 |
| GET | /capabilities/describe | 描述能力 |
| GET | /instances/{broker_id}/capabilities/status | 查审批状态 |
| POST | /instances/{broker_id}/capabilities/approve | 批准能力 |
8.4 Broker 基础 API(/api/broker)
| Method | Path | 说明 |
|---|---|---|
| GET | /brokers | 列注册 broker |
| POST | /brokers/{id}/register | 注册 |
| POST | /brokers/{id}/deregister | 注销 |
| POST | /brokers/{id}/scopes | 设置 consent scope |
| GET | /brokers/{id}/card | 取 cluster card |
| POST | /brokers/{id}/actions/{action} | 触发 action |
| POST | /brokers/{id}/deals/{deal_id}/transition | 交易状态迁移 |
| GET | /brokers/{id}/deals | 列交易 |
| GET | /brokers/{id}/ledger | 查账本 |
8.5 Federation(/api/broker/federation)
| Method | Path | 说明 |
|---|---|---|
| POST | /federation/receive | 接收跨集群消息 |
| GET | /federation/status | 联邦状态 |
| POST | /federation/rooms/{broker_id}/{room_id}/federate | 房间联邦同步 |
| POST | /federation/threads/{broker_id}/{thread_id}/federate | 线程联邦同步 |
| POST | /federation/tasks/{broker_id}/{task_id}/federate | 任务联邦同步 |
8.6 资产与 SDK(/api/broker_app)
| Method | Path | 说明 |
|---|---|---|
| GET | /{broker_id}/ui/{rel:path} | 取 UI 静态资产 |
| GET | /{broker_id}/cover | 取封面 |
| GET | /{broker_id}/sdk.js | 取 SDK shim |
| GET | /_meta/sdk.js | SDK 元数据 |
9. 安装来源与隔离语义
| 来源 | 语义 | 包级别 | 示例 |
|---|---|---|---|
built_in | 仓库随附参考包 | 可写 / 可打开目录 | starter_broker、ventaris |
user_created | 本机从零搭建 | 可写 / 可打开目录 | Workshop「新建」 |
imported_zip | 本机导入的 zip | 视为本地开发,可二次修改 | 用户导入 zip |
store | 市场下载 | 只读黑盒 | 市场安装的 Ventaris |
store 包保护规则:
- 删除 → 403
- 覆盖 → 仅当签名作者 pubkey 与已装行一致时放行
- scaffold 撞名 → 拒绝
- 运行时数据照常开放
10. 常见坑
| 坑 | 症状 | 解决 |
|---|---|---|
manifest.id 与目录不一致 | 安装失败 | broker_id 必须 == 目录名 == zip 名前缀 |
| 改 manifest 没 re-sign | 校验失败 | broker sign 重签 |
| 发布到 relay 后不进 index | 还在审核队列 | 等运营 approve |
plugin 加 sdk 参数 | 沙箱模式崩 | 只用 on_*(payload) 单参合约 |
extra_modules 申请了但没审批 | 能力未授予 | 安装时弹审批框,需用户同意 |
| store 包想覆盖 | 403 | 只能同作者升级 |
参考源码
- Hub:
src/broker/hub.py - Workshop 打包:
src/broker/workshop/packaging.py - Plugin 加载:
src/broker/workshop/plugin_loader.py - 沙箱 AST:
src/broker/workshop/sandbox/ast_guard.py - 沙箱 host/worker:
src/broker/workshop/sandbox/{host,worker}.py - REST:
src/bridge/routes/broker_workshop.py、src/bridge/routes/broker.py、src/bridge/routes/broker_app.py - 模板:
workspace/manager/brokers/starter_broker/ - SDK 文档:
workspace/manager/brokers/SDK.md - 完整开发手册:
BROKER_DEVELOPMENT.md
