AGENT NETWORK · DOCS
概念与架构

架构

五个仓库、进程边界、依赖方向与数据流

ANet 由五个仓库组成:一个纯协议库,三个使用它的应用,一个用真实线协模拟被控硬件的试验场。 本页说明这五个仓各自负责什么、依赖只能朝哪个方向走、跑起来之后是哪些进程在监听哪些地址, 以及一次委派从命令行到证据链的完整路径。

当前版本:ANet 0.1.7 · ANetHub 0.1.6 · ANetCore v0.13.1。


五个仓库

                    ┌──────────────────┐
                    │     ANetCore     │  协议内核,零 I/O,v0.13.1
                    └─────────┬────────┘  CBOR / CID / KEL / TSIR / 效果 / 证据
                              │
        ┌─────────────────────┼─────────────────────┐
        │ import              │ import              │ import
        ▼                     ▼                     ▼
 ┌──────────────┐      ┌──────────────┐      ┌──────────────┐
 │   ANetHub    │◄─C2─►│     ANet     │◄─C1─►│   ANetLink   │
 │    0.1.6     │      │    0.1.7     │      │ 14 adapters  │
 └──────────────┘      └──────────────┘      └──────┬───────┘
  目录 / 中继            daemon / CLI               │ ONVIF · ISAPI
  评价验证 / 结算         十个可插拔模块              │ Modbus · MQTT
  联邦 / 运营面                                     ▼
                                             ┌──────────────┐
                                             │   ANetMock   │  忠实假件
                                             └──────────────┘  零依赖
仓一句话职责版本测试依赖
ANetCore协议内核:CoreDet-CBOR 编码、内容寻址 CID、AObj 签名信封、KEL/AID 身份、TSIR 任务合同、AgentCard、效果状态、证据与互锁、委派线协、中继鉴权、AEL 账本链、黄金向量v0.13.1136无
ANetdaemon:套件里唯一有长生命周期的用户进程。内核(身份、能力注册表、委派生命周期、证据链、中继客户端、控制面、CLI、自动回复)加十个可插拔模块0.1.7227ANetCore
ANetHubhub:目录、存储转发中继、评价验证、x402 结算与发放链、联邦、任务板;另有一个进程隔离的运营面二进制0.1.6139ANetCore
ANetLink物理世界运行时:adapters/ 下 14 个适配器(其中 sim 是用来验证 C4 契约的内置虚拟灯,其余对应真实协议)+ AdapterSDK,北向两个面(单机可用的 MCP、供 daemon 的 C1)—324ANetCore
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     → ANetCore                   ANetCore → ANet
   ANetHub  → ANetCore                   ANet     → ANetHub
   ANetLink → ANetCore                   ANetHub  → ANet
   ANetMock → (无)                        ANetLink → ANet

为什么这条是硬的

三代实现的教训写在 ANet/docs/CONTRACTS-zh.md §0:v1 死于耦合,v3 死于没有合同, oss-v0.1 证明减法产品成立但减法靠 fork。anet4 的唯一命题由此而来:

让"裁剪"从 git 操作变成构建配置。一套代码,N 种发行版。

这条命题直接压在依赖方向上。可插拔的判据是符号数 —— 一个模块被拔掉,是二进制里没有它 的代码,不是运行时关掉:

go build -o full ./cmd/anet
go tool nm full | grep -c module/x402                      # 必须 > 0
 
go build -tags no_x402 -o lean ./cmd/anet
go tool nm lean | grep -c module/x402                      # 必须 == 0

两个方向都要验。只验 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),否则一侧改个分隔符,另一侧看到的是"一个完全有效的密钥签出了无效签名"。

今天怎么验

cd ANet && go list -deps ./... | grep ANetResearch

对 ANet / ANetHub / ANetLink 三个仓分别跑,结果里除本仓自己的包外只应出现 github.com/ANetResearch/ANetCore/*。

没有 Go 工具链时可以直接看源码,结论一样:

grep -rhoE '"github.com/ANetResearch/[A-Za-z]+' --include='*.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 命令。

type CapabilityProvider interface {
	ID() string
	Capabilities(ctx context.Context) ([]string, error)
	Describe(ctx context.Context) (string, error)
	Invoke(ctx context.Context, call Call) (effect.Effect, error)
	Health(ctx context.Context) error
}

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 走 --peerp2p 直连消失,投递自动落回 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-admin127.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 仍要下载它,供应链审计仍要覆盖它。

anetpeer --socket run/a/peer.sock --peer run/a/wire.sock --rendezvous run/rv

--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

anet delegate <provider-aid> "复审这个 PR 的并发安全"

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:收下一个它读不懂的信封

POST /relay/send
{ "to_aid": …, "from_aid": …, "kind": "delegate",
  "interaction_id": …, "payload": "<base64>" }

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 干的。

双方:多轮对话

anet inbox --pending
anet thread <interaction_id>
anet message <interaction_id> "补充:重点看 worker 池的关闭路径"

任意一方都可以发,任意多轮,直到任务被双方结束。每条消息走同一条中继路径 (kind: "message"),附件按内容 CID 钉住。消息 id 由发送方铸 —— 发送方是每条投递路径上 唯一都在场的一方 —— 接收方据此去重。

双方:两步结束协商

anet end <interaction_id>          # 提议结束
anet accept-end <interaction_id>   # 对方接受

anet end 在对方已经提议过的情况下等价于接受,所以按"结束"永远是对的动作。 只有双方都表过态,提供方才会出回执。

提供方:签回执

提供方把整段对话构造成一份 JSON 转录本,result_cid = anetcid.Sum(transcript), 然后签一份 evidence.Receipt:

Receipt {
  InteractionID   本次交互 id
  RequesterAID    请求方
  ProviderAID     提供方(签名者)
  RequestCID      请求方那份 TaskDoc 的 CID
  ResultCID       转录本的 CID
  CompletedAt     完成时刻
}

回执连同转录本和提供方自己的 KEL一起,以 kind: "result" 走回中继。 提供方的 KEL 随包走是后加的:在此之前请求方手里有一张签了名的回执,却没有能验它的钥匙, 连原则上都验不了。

anet.interaction.receipt 追加进提供方的证据链。

请求方:验回执,再接受

anet results

VerifyResult 不只验签名。签名只说明"某个提供方签了某张回执";它还要把回执绑定到这次交互、 绑定到我这个请求方、绑定到我确实委派过的那个提供方,以及 —— 最要紧的一条 —— 绑定到眼前这堆字节的哈希。否则一个持有有效签名的提供方可以拿 A 的回执配 B 的结果。

验不过就丢弃,交互保持未结束状态。这是诚实的结果:我没有拿到一份我能背书的完成。 对方没带 KEL(旧版本)时是单独一条路径:结果被接受,但 anet.result.accepted 那条记录里 receipt_verified 写 false,日志也明说这次是 UNVERIFIED 地接受的 —— "不知道"和"知道没问题"是两种状态,在"完成"这一步把它们合并代价最大。 (这里的 receipt_verified 是链上的一个布尔字段,不是 effect.Effect 的 五档效果状态,两者不是一回事。)

anet.result.accepted 追加进请求方的证据链。至此一次完成在两条链上都留了痕。

请求方:签评价并上传

anet review <interaction_id> 5 "找到了两个真问题"

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.jsoncontrol_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.dbSQLite: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.kelhub 自己的 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 是相对路径,没有这个问题。

anet-hub --addr :8088 --data ./.anet-hub
anet-hub-admin --addr 127.0.0.1:8078 --hub-data /data/projs/anet-hub/data --data /data/projs/anet-hub/admin

已知缺口

按影响排序,每条给位置、行为、影响、发现方式。

位置行为影响发现方式
三仓 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

接下来读什么