架构
五个仓库、进程边界、依赖方向与数据流
ANet 由五个仓库组成:一个纯协议库,三个使用它的应用,一个用真实线协模拟被控硬件的试验场。 本页说明这五个仓各自负责什么、依赖只能朝哪个方向走、跑起来之后是哪些进程在监听哪些地址, 以及一次委派从命令行到证据链的完整路径。
当前版本:ANet 0.1.7 · ANetHub 0.1.6 · ANetCore v0.13.1。
五个仓库
| 仓 | 一句话职责 | 版本 | 测试 | 依赖 |
|---|---|---|---|---|
| ANetCore | 协议内核:CoreDet-CBOR 编码、内容寻址 CID、AObj 签名信封、KEL/AID 身份、TSIR 任务合同、AgentCard、效果状态、证据与互锁、委派线协、中继鉴权、AEL 账本链、黄金向量 | v0.13.1 | 136 | 无 |
| ANet | daemon:套件里唯一有长生命周期的用户进程。内核(身份、能力注册表、委派生命周期、证据链、中继客户端、控制面、CLI、自动回复)加十个可插拔模块 | 0.1.7 | 227 | ANetCore |
| ANetHub | hub:目录、存储转发中继、评价验证、x402 结算与发放链、联邦、任务板;另有一个进程隔离的运营面二进制 | 0.1.6 | 139 | ANetCore |
| ANetLink | 物理世界运行时:adapters/ 下 14 个适配器(其中 sim 是用来验证 C4 契约的内置虚拟灯,其余对应真实协议)+ AdapterSDK,北向两个面(单机可用的 MCP、供 daemon 的 C1) | — | 324 | ANetCore |
| ANetMock | 用真实线协(ONVIF SOAP、海康 ISAPI、大华 CGI、Modbus TCP、MQTT、zigbee2mqtt)模拟按种子生成的整栋楼设备,状态闭环、有厂商型号身份 | — | 45 | 无 |
测试数是顶层测试函数计数(grep -rE '^func Test[A-Z_]' --include='*_test.go'),不含子测试与表驱动用例。
ANetCore / ANetHub / ANetLink 三列与 ANet/docs/DESIGN-zh.md §3 一致;ANet 与 ANetMock 两列
DESIGN 写的是 224 / 43,按当前树数出的是 227 / 45,以本页的数为准(本页写作时没有跑测试套件,
数的是源码里的函数)。
两处口径要注意:ANet 的 227 里有 18 个在 module/shell,只有 -tags shell 才编译得到;
ANetLink 的 324 里有 20 个带 //go:build realworld,要对着真设备加 -tags realworld 才跑。
默认 go test ./... 因此分别少跑这些。ANetCore / ANetHub / ANetMock 没有 tag 门控的测试文件。
十四个适配器(ANetLink/adapters/):bacnet ble btmesh can dahua habridge
hikvision modbus mqttbridge onvif opcua sim thread zigbee。
sim 是一盏内置虚拟灯,存在的目的是验证 C4 契约、让任何环境都有一台可调用的设备。
Matter 栈不在 adapters/ 下,是顶层的 ANetLink/matter/。
十个可插拔单元:减法 p2p anetlink blackboard org cas service mcp x402
taskboard(默认编进去,-tags no_<name> 去掉),加法 shell(默认不在,-tags shell
才编进去)。其中九个是 ANet/module/ 下的目录;mcp 是例外,它挂在 cmd/anet/mcp.go 与
internal/mcpserv 上,no_mcp 换进 cmd/anet/mcp_stub.go。ANet/module/ 另有两个不带 tag
的不变式守卫 inv1(org 数据不得进 p2p 面)/ inv2(org 数据不得进公开发布面)。
九个减法 tag 在 .github/workflows/ci.yml 的 pluggable 矩阵里逐个单独构建并测试,
shell 由 optin job 反向验。
ANetMock 的 go.mod 里一条依赖都没有 —— 它刻意不说 ANetCore 的类型。
一个"忠实假件"如果引用了被测方的类型,它就会跟着被测方一起错。
依赖方向:唯一合法形态
三个应用 → ANetCore,永不反向;应用之间永不 import。
为什么这条是硬的
三代实现的教训写在 ANet/docs/CONTRACTS-zh.md §0:v1 死于耦合,v3 死于没有合同,
oss-v0.1 证明减法产品成立但减法靠 fork。anet4 的唯一命题由此而来:
让"裁剪"从 git 操作变成构建配置。一套代码,N 种发行版。
这条命题直接压在依赖方向上。可插拔的判据是符号数 —— 一个模块被拔掉,是二进制里没有它 的代码,不是运行时关掉:
两个方向都要验。只验 lean == 0 会让一个从未被链接的模块看起来"可插拔"。
如果 ANet 引用了 ANetHub 的包,-tags no_x402 这类裁剪立刻失效:hub 的类型会把它整棵依赖
树拖进每一个 daemon 构建,而这些代码里没有一个 tag 是 daemon 能控制的。同理,如果 ANetCore
反向引用了任何一个应用,那"纯协议库"就不再纯,一个只想验证一张回执的第三方也得把整个 daemon
编进去 —— 那正是 anet verify 刻意避开的:它在解析 daemon 之前处理,因为检查一张回执只需要
一张回执和一份密钥历史,再多要求一样,"任何人都能验证"就变成"任何运行我们软件的人才能验证"。
三个应用同时钉在 ANetCore v0.13.1。三仓跨语言、跨仓库的线协字段由契约测试钉住
(ANet/internal/hubapi、ANetHub/internal/aghub/wirecontract_test.go、
ANetCore/relayauth),否则一侧改个分隔符,另一侧看到的是"一个完全有效的密钥签出了无效签名"。
今天怎么验
对 ANet / ANetHub / ANetLink 三个仓分别跑,结果里除本仓自己的包外只应出现
github.com/ANetResearch/ANetCore/*。
没有 Go 工具链时可以直接看源码,结论一样:
三个仓分别只回两个前缀 —— 本仓自己,加 ANetCore。go.mod 的 require 块也只列 ANetCore
一条 ANetResearch 依赖。
这条约束今天没有自动化守卫。 三个仓的 CI job 分别是:ANet test / race / optin /
pluggable / scenario,ANetHub test / unplug / race / webui,ANetLink test /
realworld / plugin / unplug / race。没有一条检查依赖方向。
判据实际落在 go.mod:三个应用的 require 块里 github.com/ANetResearch/* 只有 ANetCore
一条,加一条兄弟仓依赖会被
go mod tidy 写进 go.mod、在评审里看得见 —— 但没有机器在拦。位置:三个仓的 CI 配置;
行为:依赖方向靠人工评审;影响:一次疏忽的 import 要到有人读 go.mod 才会被发现;
发现方式:为写这份文档逐仓核对 CI job 清单。
同一条红线在进程内的形态:C1
依赖方向管的是仓与仓,C1 管的是概念不许穿过接缝:daemon 不得知道"设备"这个概念。
ANet/module/anetlink 注册的是一个 provider.CapabilityProvider,交给 daemon 的是能力 id;
daemon 解析、调用、记录效果,和对待任何其他 provider 完全一样。它不知道背后是一台摄像机、
一个 Modbus 寄存器,还是一个 shell 命令。
Describe 是这个接口上今天还空着的一格:注释说它返回描述对象在本地 CAS 里的 CID,而树里
每一个实现都返回 "" —— 内置模块直接 return "", nil,ANetLink 的 provider 转发
GET /v0/describe,而 ANetLink/northbound/c1serv 对这个路由写死了 {"cid": ""}
(源码里标着 DeviceCatalog CAS object: later)。接口形状定了,CAS 目录还没有成为描述的载体,
ANet/docs/DESIGN-zh.md §4 也是这么记的。
同一条线也画在传输上:module/p2p 是一个客户端,对 daemon 呈现为一个普通的
module.Transport,daemon 学不到 peer id、multiaddr 或 pubsub topic。
进程与端口
一个完整部署最多是六个进程:anet daemon、anetpeer、anetlinkd、anet-hub、
anet-hub-admin,加上按需被 IDE 拉起的 anet mcp。大多数节点只跑第一个。
下表按监听面列,不完全等于进程:第二行的 x402 兑付口不是独立进程,是 daemon 里的
第二个监听器;anet mcp 走 stdio,不出现在表里。
| 监听面 | 默认监听 | 谁能连 | 拔掉会怎样 |
|---|---|---|---|
anet daemon(或 anet up 后台版) | 127.0.0.1:39811,配置键 control_addr | 只有本机;bearer token 存在 control_token.txt | 整个节点停摆:CLI 报"连不上 daemon"并打印它试过的地址;信箱不再被取,一小时后 hub 把该节点标为 quiet;本地账本与证据链原样保留 |
| daemon 内的 x402 兑付口 | 不开,modules.x402.voucher_addr 与 voucher_url 必须一起设(只设一个模块拒绝加载:一个是监听在哪,一个是签进卡片的对外地址) | 公网:买家凭 hub 签名的 voucher 直接兑付 POST /x402/redeem | 已付款的买家兑不了付,委派路径不受影响;不卖能力的节点本来就不该开 |
anetpeer | 不开,--socket(daemon 侧 UDS)与 --peer(对等侧 UDS 或 TCP)都要显式给 | daemon 走 UDS;其他 anetpeer 走 --peer | p2p 直连消失,投递自动落回 hub。传输表里 hub 永远在最后一位,不会被移除 |
anetlinkd | 不开,--c1-socket <path> 才提供 C1 面 | 只有 daemon(UDS) | 配了 providers.anetlink.socket 的 daemon 起不来。 模块注册握手失败在 startModules 里是致命错误 —— 编译进来了、也配置了、却连不上,算操作者的配置问题;带着缺失的能力上线才是那种要等一次委派找不到 provider 才暴露的失败。想在没有 anetlinkd 的情况下启动,先去掉该配置块 |
anet-hub | :8088,--addr 可改 | 公网,生产上由 nginx 前置 TLS(仓库里的 deploy/nginx-hub.conf) | 目录查不到、投递投不出、评价传不上。p2p 还能不能投递取决于会合点:会合点是共享目录时两个节点仍能直连,会合点是 hub 时地址查询也一起停(anetpeer 只缓存一分钟)。发现与密钥历史无论哪种都在 hub 上 |
anet-hub-admin | 127.0.0.1:8078,--addr 可改 | 只有本机;生产上由 nginx location ^~ /admin 前置(仓库里的 deploy/nginx-admin.locations) | 运营面不可用;公网 hub 完全不受影响 —— 这正是它是第二个二进制的原因 |
anet mcp 走 stdio,由 Claude Code / Cursor 这类客户端启动,不开任何端口,会话结束即退出。
几处容易读错的地方
daemon 自己不开 p2p 端口。 监听对等连接的是 anetpeer 进程。这是刻意的:libp2p 栈会把
一千多个模块的依赖树拖进 go.mod,而 build tag 只能从二进制里去掉代码,永远去不掉
go.mod 里的依赖 —— 每次构建仍要解析它,CI 仍要下载它,供应链审计仍要覆盖它。
--rendezvous 收两种值:一个共享目录,或一个 hub URL。目录形态下 anetpeer 自己写条目,
两台机器没有共享目录就用不上;hub 形态下发布地址的是 daemon(anet p2p-advertise),
因为发布是一句关于身份的签名陈述,而 anetpeer 刻意不持有密钥 —— 它是替别人转发流量的那个进程。
hub 侧 POST /agents/{aid}/p2p 用 relayauth 挑战验签后才收,anetpeer 只读
(GET /agents/{aid}/p2p,查不到时按对方的 home hub 再问一跳,只一跳),答案缓存一分钟。
无论哪种形态,都没有 NAT 穿透、没有中继、没有会合点之外的发现:对端必须是可直接拨通的。
这是 ANet/tools/anetpeer/main.go 自己写明的缺口。(该文件的包注释仍写着"会合点还是一个共享
目录",没有跟上 hub 形态的加入 —— 以 --rendezvous 的 flag 说明与 isURL 分支为准。)
控制面只有 bearer token 保护,明文 HTTP。 control_addr 字段的定义处
(ANet/internal/daemon/config.go)写得很直白:除非你在前面放了 TLS 加鉴权,
否则它必须留在 127.0.0.1。29 条 API 路由全部在 token 之后;
/console、/ping、/attachment 与根路径重定向在 token 之外 —— 浏览器导航发不出
Authorization 头,能这么做的前提就是只监听回环。
同一台机器可以跑多个身份。 anet id new <name> 从 39811 起向上扫描,分配一个既没被任何
已知身份的配置占用、当下也能绑定的回环端口。每个身份有自己的密钥、数据目录和控制口。
运营面与公网 hub 是两个进程,但共用同一个 hub.db 文件。 两边都以 WAL 模式打开、
15 秒 busy timeout。运营面的读不受限,写只有两个动作(改 guest 配额、删注册),其余变更都
属于公网 hub 自己那套签名挑战 API。注册删除还有速率上限:每分钟最多 5 次。
一次委派的完整数据流
以最常见的路径为例:两个节点注册在同一个 hub 上,没有 p2p,不涉及付费。
请求方:anet delegate
CLI 把参数 POST 到本机控制面 /delegate,带上 control_token.txt 里的 bearer token。
CLI 自己不持有密钥,也不认识 hub。
请求方 daemon:签任务合同
daemon 用自己的私钥签出一份 TSIR TaskDoc,签名是分离的 aobj.Envelope(TaskDoc 的
CoreDet-CBOR 字节里不含信封,信封在旁边走)。request_cid = anetcid.Sum(doc) —— 这个 CID
是后面回执要锚定的东西。同时铸一个随机 interaction_id,双方以后都用它来指称这次交互。
请求先写进本地 interactions.db(role=outbound),目标作为第一条对话消息记下来;
附件被读入、按 anetcid.SumRaw 钉住内容、随消息一起存。
请求方 daemon:装信封,选传输
DelegateReq{TaskDoc, Envelope, KEL, InteractionID, Attachments} 用 CoreDet-CBOR 编码。
请求方自己的密钥历史(KEL)随包走,这样对方不需要问任何注册表就能独立验签。
传输按偏好顺序试:直连优先,hub 兜底。hub 永远在列表最后一位,而且不会被移除 —— 对端离线、 在 NAT 后面、或者本节点从没见过它,都由 hub 接住。报"不可达"的传输会被跳过,不去尝试; 失败的会被记下然后试下一个;全部失败才向调用者报错,而且报的是最后一次失败的原文, 不是"所有传输都失败了"这种摘要。
日志只在状态变化时打:一条传输开始失败打一行、恢复打一行,中间沉默。NAT 后面的节点每一次 委派都会走这条回退路径,一条消息一行会把日志淹掉 —— 一次基准测试里这条路径打出过二十万行 还没报完。
hub:收下一个它读不懂的信封
hub 检查收件人是不是本地注册过的 agent(拿得出 KEL),然后把 to_aid / from_aid / kind /
interaction_id 与 base64 解码后的 payload 字节存进 relay_message。
payload 它不解码。 它看得到收发双方的 AID、消息类型和交互 id,看不到目标、附件或任何
签名对象的内容。如果收件人在本 hub 不存在但联邦里有,/fed/v1/forward 转一跳。
hub 如果知道收件人已经很久没来取信,会在应答里带一句警告;请求方 daemon 把它记进日志 —— 一条没人读的警告等于没有警告。
请求方 daemon:先在自己的链上记一笔
anet.delegation.sent 追加进本地证据链(evidence.ael.jsonl),带 interaction_id、
provider_aid、request_cid。一个请求方的链应该显示它要过什么,而不只是它收到过什么。
提供方 daemon:取信、验签、落库
daemon 每秒轮询一次 POST /relay/poll(ANet/internal/daemon/relay.go 的
relayPollInterval = 1 * time.Second,就是驱动那个 ticker 的常量。别把它和
auto_reply.poll_interval_seconds 的默认 5 秒搞混,后者是自动回复扫描的节奏,不是取信的)。
这个请求本身要签名:签的是
"anet-relay/" + action + "/" + aid + "/" + ts,五分钟重放窗口。四个动作(poll / ack /
register / profile)各自独立 —— 一个抓到的 poll 签名不能拿来改别人的公开资料。
拿到 DelegateReq 后,VerifyDelegateReq 从随包的 KEL 推出 AID,再用信封验 TaskDoc 的签名。
验不过就丢弃,不是存下来标记为可疑。验过的密钥历史被记住,以后模块问"是谁的密钥签的这个"
才有答案。落库之后才 POST /relay/ack 把它从信箱里清掉。
配置里 accept_delegations 为 false 的节点直接丢弃。接受也只是存下来:
anet 不跑模型,活是操作者自己的 agent 干的。
双方:多轮对话
任意一方都可以发,任意多轮,直到任务被双方结束。每条消息走同一条中继路径
(kind: "message"),附件按内容 CID 钉住。消息 id 由发送方铸 —— 发送方是每条投递路径上
唯一都在场的一方 —— 接收方据此去重。
双方:两步结束协商
anet end 在对方已经提议过的情况下等价于接受,所以按"结束"永远是对的动作。
只有双方都表过态,提供方才会出回执。
提供方:签回执
提供方把整段对话构造成一份 JSON 转录本,result_cid = anetcid.Sum(transcript),
然后签一份 evidence.Receipt:
回执连同转录本和提供方自己的 KEL一起,以 kind: "result" 走回中继。
提供方的 KEL 随包走是后加的:在此之前请求方手里有一张签了名的回执,却没有能验它的钥匙,
连原则上都验不了。
anet.interaction.receipt 追加进提供方的证据链。
请求方:验回执,再接受
VerifyResult 不只验签名。签名只说明"某个提供方签了某张回执";它还要把回执绑定到这次交互、
绑定到我这个请求方、绑定到我确实委派过的那个提供方,以及 —— 最要紧的一条 ——
绑定到眼前这堆字节的哈希。否则一个持有有效签名的提供方可以拿 A 的回执配 B 的结果。
验不过就丢弃,交互保持未结束状态。这是诚实的结果:我没有拿到一份我能背书的完成。
对方没带 KEL(旧版本)时是单独一条路径:结果被接受,但 anet.result.accepted 那条记录里
receipt_verified 写 false,日志也明说这次是 UNVERIFIED 地接受的 ——
"不知道"和"知道没问题"是两种状态,在"完成"这一步把它们合并代价最大。
(这里的 receipt_verified 是链上的一个布尔字段,不是 effect.Effect 的
五档效果状态,两者不是一回事。)
anet.result.accepted 追加进请求方的证据链。至此一次完成在两条链上都留了痕。
请求方:签评价并上传
daemon 签一份 evidence.Review,里面带 ReceiptCID —— 评价锚定的是这一张回执,
不是随便一张。签完存本地,然后 POST /reviews 上传四样东西:回执、评价、请求文档、交付物,
全部 base64。少任何一样 hub 都拒收:一条评价必须带着它被验证所依据的内容。
hub:验证互锁并保存原始字节
hub 做两半检查,刻意分开:
-
只有本 hub 知道的留在 hub:这次交互是否已经被评过、评价者是否在本 hub 注册。 这是策略,不是关于对象的事实。
-
互锁本身是
ANetCore/evidence.VerifyInterlock,十项检查,依次是:回执与评价都在场、 评分在 1..5、两个对象说的是同一次交互、评价者就是请求方、被评者就是提供方、 评价锚定的正是这张回执的 CID、请求字节哈希得到回执里的RequestCID、 交付字节哈希得到ResultCID,最后才是回执签名与评价签名 (贵的放最后,结构性检查过不了的对象不值得做一次曲线运算)。这十项里没有一项需要外部状态。这是有意的 —— 交互有没有被评过、双方在哪儿注册, 是某个 hub 对着自己库执行的策略,不是关于这些对象的事实。把它们排除在外, 才使得一个只拿到几个文件的审计者能得出和 hub 一样的结论。
requestDoc或deliverable传 nil 会跳过对应的内容绑定,函数如实跳过而不假装验过。
任何一项失败,评价被拒且永不展示。通过后 hub 既存结论,也存被验证的原始字节
(review_blob) —— 一个存了结论丢了证据的 hub 只能转述自己的说法,而联邦声誉建立在
hub 相信 hub 之上正是这套设计要避免的。
hub 看得到什么、看不到什么
| hub 能 | hub 不能 |
|---|---|
| 决定谁能注册(可选的邀请码准入)、谁在目录里可见 | 伪造任何一条评价、回执、任务合同 |
| 转发它读不懂的字节,并知道收发双方的 AID、消息类型、交互 id | 读懂经它中继的签名对象的信任关键内容:目标、对话、交付物 |
| 记账、签发凭证、签结算收据 | 事后改写自己的发放链而不被已取过链头的读者发现 |
| 拒绝出售某项能力 | 改价,或把买家指到别的机器 —— 价钱读自 agent 自己签的卡片 |
| 标记一小时未取信的节点为 quiet、一个月未露面的退出可浏览列表 | 删除任何人的证据;什么都不删是静默两档的设计前提 |
x402 付费路径同理:hub 只卖门票,不代理内容。买家在 GET /x402/resource/{aid}/{capability}
未付款拿到 402,付款后拿到一张 voucher,拿着它直接找提供方的兑付口。
hub 全程见不到请求参数,也见不到结果。
存储
daemon 侧
数据目录默认 ~/.anet,环境变量 ANET_DATA_DIR 覆盖;目录权限 0700(里面有私钥)。
| 文件 | 装什么 | 丢了会怎样 |
|---|---|---|
identity.kel | 私钥种子 + 密钥事件日志。权限 0600 | 这个节点没了。 AID 由 KEL 派生,拿不回来就换了个实体:别人给它的评价、hub 上的余额、别人手里指向它的回执,全部指向一个再也签不出东西的身份 |
config.json | control_addr / hub_url / name / caps / accept_delegations / guest_messages / auto_reply / providers / modules | 下次启动写一份默认配置回去。节点还是同一个 AID,但不再连 hub、不再注册能力,得重跑 anet hub-register |
control_token.txt | 控制面 bearer token(0600)。文件在就跨重启复用,不是每次启动换一个 | daemon 下次启动生成一个新的;旧 token 失效,已经拿到 token 的进程要重读。运行中的 daemon 手里那份在内存里,删文件不会立刻踢掉它 |
evidence.ael.jsonl | 本节点的仅追加哈希链:每次能力效果、每张签出的回执、每次委派、每次接受的结果。一行一条 base64 的 CoreDet-CBOR 记录 | 本节点关于自己做过什么的证据没了。 别人手里的回执与评价不受影响 —— 它们签在别处。文件名里的 .jsonl 是历史遗留,内容是 CBOR;路径保留是为了升级的节点接着写同一条链,而不是在旁边悄悄开第二条 |
interactions/interactions.db | SQLite:interaction / message / attachment 三张表。委派出去与收进来的全部任务、完整对话、附件字节 | 对话与交付内容没了。anet thread / anet results 查不到东西;已上传到 hub 的评价与其原始字节还在 hub 上 |
daemon.log | 运行日志,anet logs 读它 | 只影响排查 |
跨进程协调放在 /tmp/anet-<uid>/(固定路径,不走 $TMPDIR,按 uid 隔离、0700):
daemon.json 是单 daemon 指针,daemons/ 每个运行中的身份一个条目。这两样是 daemon
启动时写、退出时删的运行时状态,删了不影响任何持久数据。
证据链是先验证后使用的:每次打开都用 ael.Import 重建并校验整条链。
被篡改或分叉的文件会拒绝加载,而不是安静地提供被改过的历史。
所以"手工编辑一下这个文件"不是一个可行操作 —— 结果是 daemon 起不来。
hub 侧
数据目录由 --data 指定,默认 ./.anet-hub。
| 文件 | 装什么 | 丢了会怎样 |
|---|---|---|
hub.db | 主库(SQLite,WAL)。注册表 agent / agent_cap / agent_card;信箱 relay_message;评价 review + review_blob(被验证的原始字节);账本 credit_balance / credit_entry / credit_settled / credit_cleared;发放与兑付链 credit_issuance / credit_redemption;见证 head_attestation;跨 hub hub_owed / hub_due / hub_cleared / fed_card / fed_review;p2p 地址目录 p2p_addr;注销留档 departed_kel;完成计数 completed_task;准入 invite / invite_use / hub_setting | 这个 hub 上的目录、未取的信、评价和余额全没了。 节点自己的身份、本地账本、证据链不受影响,它们在各自机器上。评价的原始字节在这里,所以丢了它等于丢掉这个 hub 能向联邦出示的证据 |
hub_identity.kel | hub 自己的 AID 与密钥历史,GET /hub/identity 公开的那份 | hub 换了身份。联邦对端 pin 过旧 KEL,信任锚断裂;hub 签过的结算收据再也验不回它 |
guest_identity.kel | 访客模式代签用的身份 | 访客会话的历史对不上 |
federation.json | 联邦配置:投递档位与对端清单。文件不存在 = 联邦关闭 | 联邦静默关闭,本地功能不受影响 |
federation.db | 联邦游标与 pin 住的对端 KEL | 目录与评价同步从头再来 |
taskboard.db | 任务板卡片与事件流,独立于 hub.db(所以模块可以整个拿掉而不动主库) | 任务板清空,其余不受影响 |
运营面侧
anet-hub-admin --data 下是 admin.db 与 datasets/:运营面自己的状态、审计记录
(每一次变更动作的 who/what/when)、统计快照与数据集归档。它另外用 --hub-data 打开公网 hub 的
hub.db,从不创建也从不迁移那个库。
这两个 flag 的默认值是 /data/projs/anet-hub/admin 与 /data/projs/anet-hub/data ——
一台特定机器的部署路径被写进了 flag 默认值。对别的部署这个"默认"没有意义,两个都得显式给。
anet-hub 的 --data 默认 ./.anet-hub 是相对路径,没有这个问题。
已知缺口
按影响排序,每条给位置、行为、影响、发现方式。
| 位置 | 行为 | 影响 | 发现方式 |
|---|---|---|---|
| 三仓 CI | 没有一条 job 检查依赖方向 | 一次疏忽的兄弟仓 import 要到有人读 go.mod 才会被发现 | 为写本页逐仓核对 CI job 清单 |
anetpeer 的发现面 | 会合点收共享目录或 hub URL,两种都要求对端可直接拨通 | 没有 NAT 穿透、没有中继、没有会合点之外的发现;NAT 后的节点仍然只能走 hub 中继 | 读 ANet/tools/anetpeer/main.go 的 isURL / lookupHub 分支;该文件的包注释本身已经陈旧 |
| p2p 强制模式 | 现为"能直连就直连,不能就走 hub"的自动回退,无开关 | 无法表达"注册只为发现、投递全走 p2p" | ANet/docs/DISTRIBUTIONS-zh.md |
C1 的 Describe | 接口约定返回描述对象的 CID,树里每个实现都返回 "" | 能力描述今天不经 CAS 走,ADP 卡片上挂不到 descriptor | 逐个读 provider/ 与 module/*/ 的实现,以及 ANetLink/northbound/c1serv 写死的 {"cid": ""} |
| ANetLink 真机覆盖 | 真机 PTZ / 事件 / 抓拍未测;厂商云适配器为零;自动发现只有 ONVIF | 设备生态覆盖 | ANet/docs/SUITE-TODO-zh.md L-1 / L-2 / L-3 |