AGENT NETWORK · DOCS
参考

构建标签参考

十个标签、两个方向、CI 判据

这一页是查表用的:ANet、ANetHub、ANetLink 三个仓库里每一个构建标签的名字、方向、 管辖的代码位置、去掉(或加上)之后具体失去什么,以及验证它的两条命令。

机制与设计理由在 可插拔,分档取舍在 发行版总览。这里只给事实。


一 · ANet daemon:十个标签

anet 有十个构建标签,九个减法、一个加法。

名字标签方向代码位置注册文件
serviceno_service减法(默认在)module/servicecmd/anet/module_service.go
x402no_x402减法module/x402cmd/anet/module_x402.go
mcpno_mcp减法internal/mcpserv、cmd/anet/mcp.gocmd/anet/mcp.go、cmd/anet/module_mcp_declare.go
p2pno_p2p减法module/p2pcmd/anet/module_p2p.go
taskboardno_taskboard减法module/taskboardcmd/anet/module_taskboard.go
casno_cas减法module/cascmd/anet/module_cas.go
blackboardno_blackboard减法module/blackboardcmd/anet/module_blackboard.go
orgno_org减法module/orgcmd/anet/module_org.go
anetlinkno_anetlink减法module/anetlink、provider/anetlinkcmd/anet/module_anetlink.go
shellshell加法(默认不在)module/shellcmd/anet/module_shell.go

注册文件本身带着模块的标签(//go:build !no_service 之类),里面只有一行 blank import (mcp 是例外,见下面的说明)。 这就是标签成为开关的全部机制:文件不编译 → 包不被 import → init() 不跑 → module.Register 没被调用 → 链接器没有理由留下这个包。

mcp 是九个减法名字里唯一不挂在 module.Host 上的。它是一个 northbound (anet mcp,stdio 上的 MCP 服务),代码在 internal/mcpserv,由 cmd/anet/mcp.go 的 //go:build !no_mcp 管辖。它不进模块注册表,但仍然出现在 anet version 的 modules: 一行里:cmd/anet/module_mcp_declare.go 带同一个 //go:build !no_mcp, 里面只有一句 module.DeclareCompiled("mcp"),所以这一行报的是链接器留下了什么。 CI 数它的符号要特判成 internal/mcpserv。


二 · 九个减法标签逐条

体积数字取自 发行版总览 的实测表 (2026-09-03,go 1.26.7,linux/amd64,CGO_ENABLED=0,-trimpath -ldflags "-s -w")。 每一行都是从默认构建出发只加一个 no_<name> 量出来的,单位 KiB。

no_service

  • 去掉:module/service
  • 失去的能力:把本机已有的 HTTP 服务声明成 ANet 能力对外提供。daemon 收到调用后 把参数 POST 到配置里写的 URL、读回复,服务端不需要懂 AID、收据或 CBOR。
  • 攻击面变化:少一条出站路径(向配置里写明的 HTTP URL 发请求)。不涉及监听口。
  • 体积:省 12 K,依赖减 1
  • 注意:这是"我有一个服务,怎么挂上网络"的唯一答案。去掉之后这个节点只能委派和接活, 不能以能力 id 对外提供服务。

no_x402

  • 去掉:module/x402 全部 —— facilitator 客户端、报价、结算、凭证签发、 兑付路径、对账与发放链审计
  • 失去的能力:标价、被付款、付款、anet balance、anet redeem、 anet reconcile、anet audit-hub
  • 攻击面变化:最大的一条。 x402 是九个模块里唯一在 anet 进程里 net.Listen 的模块 —— module/x402/voucher.go 的 startRedeemFace, 只在配置里 voucher_addr 非空时执行。no_x402 的构建里这个监听口的代码不存在。
  • 体积:省 116 K,依赖减 1
  • 无模块构建上的行为:五个付费控制面入口 —— /balance、/redeem、 /x402-authorize、/reconcile、/audit-hub —— 统一回答 internal/daemon/paylink.go 的 errNoPayments():
anet: this build has no payment support (built with -tags no_x402, or the x402 module is not configured)

被要求做标价的能力时答 UNAVAILABLE 并说明原因,不会免费替人干活。 UNAVAILABLE(做不了)与 FAILED(试了没成)是两种状态,不合并 —— 见 诚实效果状态。

no_mcp

  • 去掉:internal/mcpserv、cmd/anet/mcp.go 与 cmd/anet/module_mcp_declare.go, 连同 MCP Go SDK 及其 jsonschema 依赖树
  • 失去的能力:anet mcp 的 MCP stdio 服务
  • 攻击面变化:无监听口的变化。anet mcp 是本机 MCP 客户端通过 stdio 拉起的子进程, 只连本机 daemon 的回环控制口。
  • 体积:省 1,896 K,依赖减 34 —— 占九个模块可省总量的 85%, 是唯一一个体积理由成立的标签。省下来的主要不是自己的代码,是 SDK 的依赖树。

anet mcp 这条命令在 no_mcp 构建里仍然存在,由 cmd/anet/mcp_stub.go 提供替身, 回答:

anet mcp: this build was compiled with -tags no_mcp

给出这句话而不是"未知命令",是因为一个在 MCP 客户端里配好了 anet mcp 的操作者 需要知道问题出在哪个构建上。

no_p2p

  • 去掉:module/p2p(daemon 侧的对等传输客户端)
  • 失去的能力:委派直连投递。去掉之后所有投递走 hub 中继。
  • 攻击面变化:daemon 少一条出站 unix socket 连接。入站对等连接不在 anet 里 —— daemon 只是通过 unix socket 连到 anetpeer 进程,真正监听的是 anetpeer。 要拔掉"这台机器接受入站对等连接",停的是 anetpeer 进程。
  • 体积:省 28 K,依赖减 1
  • 注意:anet p2p-advertise 不在这个标签管辖之下。发布直连地址是一条 关于本节点身份的签名声明,发给自己的 hub,在内核里、无标签 (internal/daemon/hub_client.go 的 AdvertisePeerAddress)。 一个 no_p2p 的构建照样能发布地址;消费这个地址的才是模块。

no_taskboard

  • 去掉:module/taskboard(hub 共享任务板的客户端)
  • 失去的能力:三个能力 task.board(读板)、task.create(放活)、task.claim(领活)
  • 攻击面变化:少一条出站路径(向本节点的 hub 发签名请求)。无监听口。
  • 体积:省 24 K,依赖减 1
  • 注意:板本身在 hub 上(ANetHub 的 no_taskboard 管的是服务端,见第八节)。 hub 侧的板有读板、单卡查询和八个变更动作 (create / move / claim / submit / accept / reject / block / unblock, 见 ANetHub/internal/taskboard/http.go);daemon 侧的模块只把其中三个做成能力。 移动、阻塞、拒绝这些协调动作是人在 UI 里做的, 它们仍然以 HTTP 端点形式向任何会签名的东西开放。

no_cas

  • 去掉:module/cas(内容寻址存储)
  • 失去的能力:四个能力 cas.put / cas.get / cas.has / cas.stat。 CID 构造是 CIDv1 dag-cbor sha2-256。
  • 攻击面变化:无。这个包里没有 net/http 也没有 net.Dial。
  • 体积:省 52 K,依赖减 1

no_blackboard

  • 去掉:module/blackboard(共享认知板)
  • 失去的能力:三个能力 blackboard.add / blackboard.snapshot / blackboard.conclude
  • 攻击面变化:无网络行为
  • 体积:省 36 K,依赖减 1
  • 注意:板上的贡献是逐条验签的(合并前要知道"是谁签的"), 这正是 module.Host 上 ResolveKEL() 这一项存在的原因。

no_org

  • 去掉:module/org(组织成员凭证)
  • 失去的能力:两个能力 org.verify / org.info
  • 攻击面变化:无网络行为
  • 体积:省 28 K,依赖减 1
  • 注意:这是接缝存在的理由本身。v1 的组织功能长进 35 个 daemon 文件, 1.3.0-v3 开源时删掉 683,852 行才拔出来。现在它是一个 -tags no_org。
  • 去掉:module/anetlink 与 provider/anetlink
  • 失去的能力:把 ANetLink 运行时触达的设备能力注册成本节点的能力
  • 攻击面变化:少一条出站 unix socket 连接(连 ANetLink 的 C1 socket)。无监听口。
  • 体积:省 12 K,依赖减 2
  • 注意:去掉这个模块不会让 daemon "少懂一点设备" —— 按 C1,daemon 从来就不知道 "设备"这个概念,它只看见能力。见 五条契约。

三 · 加法标签 shell

go build -tags shell -o anet ./cmd/anet
  • 加上:module/shell
  • 得到的能力:shell.list,以及每条运营者命名的命令各一个 shell.run@<name>(前缀 shell.run@ 定义在 module/shell/shell.go)。 shell.exec(任意命令)只在配置里 allow_arbitrary 为真时才注册: 编进来、配上了、开了任意命令,是三个独立的开关
  • 参数:命名命令只接受 argv 一个 args 键,shell.exec 只接受 command 与 dir; 其他键报错,不会被静默丢弃(module/shell/provider.go 的 onlyKnownArgs)
  • 体积:加 36 K,依赖加 1
  • 公开面:不开端口。请求经 hub 中继或 p2p 到达常规委派入口。
  • 提权:不提权。命令以 daemon 自己的用户身份运行。一个以 root 启动的 daemon 能跑 root 命令 —— 这是安装时写在 unit 文件里的决定,不是这个模块能授予的。

方向为什么反过来

因为两种写错的代价不对称:

写错的方式后果
减法标签漏了(该去掉的没去掉)某人想去掉的模块留在了他自己的构建里
加法标签漏了(该不在的进去了)命令执行能力进了所有没要过它的构建

规则可以写成一句:默认构建应当是完整的,除非某个能力"进错构建"的后果比"缺失"更重。 目前套件里只有 shell 满足这个条件 —— 它是唯一一个把一次网络请求变成宿主机上一条命令的模块。 需要靠"记得去掉"来保护的人,恰好是没听说过这个标签的人。

no_shell 不存在,而这需要专门做一件事

一个操作者在配置里写了 modules.shell 而构建里没有它,如果错误信息说 "是不是用 no_shell 编的",他会去找一个从来不存在的标签。

解决办法是 cmd/anet/optin_tags.go —— 一个没有构建标签的文件,内容只有:

func init() { module.DeclareOptIn("shell") }

名字必须在不含这个模块的构建里也存在,否则没有任何东西能说出正确的修法。 于是 module.Build 的错误信息分成两种:

module "p2p" is configured but not compiled into this build (built with no_p2p?)
module "shell" is configured but not compiled into this build (it needs -tags shell, which the default build does not use)

"编进去"不等于"开着"。 带 shell 的构建在没有 modules.shell 配置块时不注册任何能力; 配了但调用方名单为空时拒绝所有远程调用;只有运营者在配置里命名过的命令能跑。 三道闸门分别由发布方、机器运营者、运营者的名单文件把守 —— 见 shell 发行变体。


四 · 组合规则

一、减法标签之间可以自由叠加,用逗号连接,不分先后。

go build -tags no_x402,no_mcp,no_p2p -o anet ./cmd/anet

二、加法标签和减法标签写在同一个串里。

go build -tags no_x402,no_mcp,shell -o anet ./cmd/anet

三、内核没有标签,拔不掉。 身份与多身份、能力注册表(C1)、委派生命周期、 委派验签、收据验证、第三方验证、证据链、hub 中继客户端、传输列表、对端 KEL 缓存、 回环控制面、CLI、自动回复、本地控制台 —— 这些没有对应的 no_ 标签, 想要一个"不带委派的 daemon"是做不到的。

四、配置了却没编进来的模块是错误,不是空操作。 module.Build 直接失败, 而不是让 daemon 读到 "p2p": {...} 之后默默忽略、留下一个操作者对着 "为什么没有在对等"发呆。

五、九个减法标签有 512 种组合,CI 只跑 18 种。 没被列进矩阵的组合可能编译不过。 自建裁剪版本时,把你的标签组合加进你自己的 CI。

CI 已验证的 18 种组合

下面是 .github/workflows/ci.yml 里 pluggable job 的完整矩阵,每次提交都跑:

#-tags覆盖意图
1no_anetlink单个
2no_p2p单个
3no_p2p,no_anetlink递增
4no_blackboard单个
5no_blackboard,no_p2p,no_anetlink递增
6no_org单个
7no_org,no_blackboard,no_p2p,no_anetlink递增
8no_cas单个
9no_cas,no_org,no_blackboard,no_p2p,no_anetlink递增
10no_service单个
11no_mcp单个
12no_x402单个
13no_taskboard单个
14no_x402,no_service,no_mcp,no_taskboard,no_cas,no_org,no_blackboard,no_p2p,no_anetlink全部九个 = min
15no_x402,no_mcp,no_taskboard,no_cas,no_org,no_blackboard,no_p2p,no_anetlinkstandard
16no_mcp,no_taskboard,no_cas,no_org,no_blackboard,no_p2p,no_anetlinkpaid
17no_x402,no_cas,no_org,no_blackboard,no_p2p,no_anetlinkagent
18no_x402,no_mcp,no_cas,no_org,no_blackboard,no_anetlinkp2p

九个减法标签每一个都有独立一行,所以每个模块都被单独数过符号。 第 15–18 行是发行版组合,专门列出来的理由写在文件注释里: 一个设计文档提出而没有人构建的组合,会在没有人注意的情况下停止编译。

加法方向不在这张表里 —— shell 由单独一个 job 验,见第六节。


五 · 判据:两条 go tool nm 命令,两个方向

判据是符号数,不是"运行时禁用"。对任意一个减法模块 <m>:

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

以 x402 为例,可以原样复制运行:

go build -o full ./cmd/anet
go tool nm full | grep -c module/x402
 
go build -tags no_x402 -o lean ./cmd/anet
go tool nm lean | grep -c module/x402

加法方向的判据一样是符号数,只是断言反过来:

go build -o a ./cmd/anet
go tool nm a | grep -c module/shell             # 必须 == 0
 
go build -tags shell -o b ./cmd/anet
go tool nm b | grep -c module/shell             # 必须 > 0

两个方向都要验。 只验 lean == 0 会让一个从未被链接进任何构建的模块看起来"可插拔" —— 它在两种构建里都是 0,而检查只看其中一边,于是通过。full > 0 这一半证明的是 "检查确实看得见它要检查的代码"。

判据是 0 与非 0,不是具体数字。符号数会随实现变化,拿它当断言只会得到一堆脆弱的检查。

符号匹配的 pattern 在 CI 里是 provider/<mod>|module/<mod>,mcp 特判为 internal/mcpserv —— 大部分模块住在 module/ 或 provider/ 下,MCP northbound 住在 internal/mcpserv,一个看不见自己要检查的代码的检查会因为错误的理由通过。


六 · CI 的两个 job

.github/workflows/ci.yml 里有两个专门管标签的 job,方向相反。另有 test(gofmt / vet / go test ./...)、race(go test -race ./...)、 scenario(一个 hub 加三个节点的真实多进程联调)三个 job 与标签无关。

pluggable —— 减法方向

18 行矩阵(上表),每一行做四件事:

go build -tags "<tags>" ./...
go test  -tags "<tags>" ./...
go build -tags "<tags>" -o /tmp/anet ./cmd/anet && /tmp/anet --help
# 然后符号数:full > 0 且 lean == 0

组合行只对第一个标签数符号(脚本里是 mod="${mod%%,*}")。这一点不影响覆盖: 九个模块各自都有独立一行,单独被数过。

optin —— 加法方向

单独一个 job,不是矩阵里多加一行。理由写在文件注释里: 一个坏掉的减法标签发出去的是"某人想删的模块没删掉",一个坏掉的加法标签发出去的是 "命令执行能力进了所有没要过它的构建",代价不同,所以断言也分开写:

gofmt -l .                       # 必须为空
go vet  -tags shell ./...
go test -tags shell ./...
 
go build -o /tmp/anet-default ./cmd/anet
go build -tags shell -o /tmp/anet-optin ./cmd/anet
/tmp/anet-default --help && /tmp/anet-optin --help
 
go tool nm /tmp/anet-default | grep -c module/shell    # 必须 == 0
go tool nm /tmp/anet-optin   | grep -c module/shell    # 必须 > 0

为什么加法标签必须另有一条 go test -tags shell ./...

因为 go test ./... 根本看不见构建标签后面的代码。

module/shell/shell_test.go 自己也带 //go:build shell。一条不写标签的测试命令 不会编译它,更不会运行它 —— 于是这个模块的测试哪儿都不会跑, 而它是套件里唯一一个在宿主机上执行命令的模块,是最不该没有覆盖的那一个。 go vet 同理:不带标签的 vet 看不见这些文件。

这是加法标签相对减法标签多出来的一项义务:新增一个加法标签时, 必须同时在 CI 里加一条带这个标签的 vet 与 test,否则这段代码没有任何检查覆盖。 减法标签没有这个问题 —— 它的代码在默认构建里,默认的 go test ./... 就编译并运行了。

跨进程的覆盖由 scripts/joint-shell.sh 补(hub + 两个 daemon, 其中提供方用 -tags shell 构建),它还会检查提供方日志里 modules compiled in 一行确实含有 shell。


七 · 本地怎么验

./build.sh --check

在 ANet/ 仓库根目录跑。它做这些事,顺序如上:

步骤命令
格式化gofmt -l -w internal cmd(自动改,并报告改了哪些文件)
静态检查go vet ./...
静态检查(加法方向)go vet -tags shell ./...
测试go test ./...
测试(加法方向)go test -tags shell ./...
符号断言构建默认二进制,断言 module/shell 符号数 == 0

最后一步放在本地而不只放在 CI,是因为这是开发者推送之前跑的脚本, 而"它默认不在"是整个加法标签所依赖的全部内容。

不加 --check 时 ./build.sh 只构建,不跑检查。CGO_ENABLED=0,无标签, 产物落在仓库根的 ./anet。

带标签构建

build.sh 不接受标签参数。要选标签用 scripts/build.sh 的 TAGS 环境变量:

bash scripts/build.sh                                  # 本机,默认标签
TAGS=shell bash scripts/build.sh                       # 加法标签
TAGS=no_x402,no_mcp bash scripts/build.sh              # 减法标签
GOOS=linux GOARCH=arm64 bash scripts/build.sh          # 交叉编译
OUT=/tmp/anet bash scripts/build.sh                    # 选输出路径

scripts/build.sh 把 commit、构建时间与标签串本身用 -ldflags -X 打进二进制。 标签串被记下来的理由是:一次发布要跑这个脚本很多次,标签串是区分产物的唯一东西, 一个说不出自己是哪个变体的二进制,会把"这台机器接不接受远程命令"变成一个靠猜回答的问题。

但不要用这个字段判断一个二进制含不含 shell。go build -tags shell 而不加 -X 会产出一个含有 shell 模块、却报告"没有标签"的二进制。可靠的做法是问注册表:

anet version
anet 0.1.7 (commit unknown, built unknown)
modules: anetlink,blackboard,cas,mcp,org,p2p,service,taskboard,x402

这一行读的是 module.Compiled() —— 一个模块在列表里,是因为它的 init() 跑了, 而 init() 跑了,是因为链接器留下了它。注册表不会说错。

MCP 不走模块注册表,但由 module.DeclareCompiled("mcp") 补进这一行,所以它也在; shell 默认不在这一行,带上 -tags shell 后会出现。九个减法标签全用上时(min 档) 这一行是 modules: (none)。

daemon 启动时把同一份清单写进日志:

anet: modules compiled in: anetlink,blackboard,cas,mcp,org,p2p,service,taskboard,x402 (started: N)

compiled in 与 started 是两个数:前者是二进制里有什么,后者是本机配置里启用了几个。 mcp 只出现在前者 —— 它不是 module.Module,没有可启动的实例。

换一组标签重新构建后重启 daemon,不需要再显式跑一次 anet hub-register:启动时 internal/daemon/daemon.go 的 refreshRegistration 会在后台按当前模块提供的能力清单 重新注册,能力的增删跟着构建走。这次刷新是非致命的 —— hub 不可达时只记一行日志, 节点照常运行,显式注册仍然可用。


八 · ANetHub:两个标签

hub 的可选子系统挂在 cmd/anet-hub/mounts.go 的 mount 接缝上 —— 一个 name 加一个 wire func(*hubDeps) (func() error, error), hubDeps 里是数据目录、store、hub 身份、aghub.Server 和根 mux。 模块看得见内核与 mux,看不见彼此。

名字标签方向注册文件去掉后失去
federationno_federation减法cmd/anet-hub/wire_federation.go/fed/v1/ 全部端点、跨 hub 目录同步、跨 hub 结算与清算、对等 KEL 解析、对等发放头见证
taskboardno_taskboard减法cmd/anet-hub/wire_taskboard.go/tasks/ 全部端点(共享任务板服务端)

no_federation 去掉的东西比端点多:它同时拿走 SetForwarder(投递转发)、 SetPeerKELResolver、SetPeerEndpointResolver、SetFederatedDirectory 以及两个后台循环(目录同步 syncLoop、发放头见证 witnessLoop)。 一个 no_federation 的 hub 是一个只服务自己注册用户的孤立 hub。

CI(ANetHub/.github/workflows/ci.yml 的 unplug job)跑三种组合:

go build -tags no_taskboard ./...
go build -tags no_federation ./...
go build -tags no_taskboard,no_federation ./...

hub 侧的标签只验编译,不验符号数。 unplug job 只有一步 go build, 没有 go test -tags,也没有 go tool nm 的两方向断言。 也就是说 hub 的两个标签目前满足"去掉之后还能编译",不满足 ANet 那条 "full > 0 且 lean == 0"的判据。ANetHub 的 scripts/build.sh 也帮不上: 它构建 anet-hub 与 anet-hub-admin 两个二进制并重建嵌入的 web UI, 不接受标签参数,不做符号断言。


九 · ANetLink:十一个减法、四个加法

ANetLink 的可选单元是适配器,挂在 cmd/anetlinkd/plugins.go 的 adapterPlugin 槽上,注册文件是 cmd/anetlinkd/plugin_<name>.go。

十一个减法标签(协议适配器,默认在)

配置名标签适配器包
modbusno_modbusadapters/modbus
mqttno_mqttadapters/mqttbridge
homeassistantno_haadapters/habridge
opcuano_opcuaadapters/opcua
bacnetno_bacnetadapters/bacnet
canno_canadapters/can
bleno_bleadapters/ble
zigbeeno_zigbeeadapters/zigbee
btmeshno_btmeshadapters/btmesh
threadno_threadadapters/thread
simno_simadapters/sim

注意配置名与标签名不总是一致:Home Assistant 桥的配置键是 homeassistant, 标签是 no_ha,包是 habridge。

sim 是唯一 defaultOn: true 的适配器 —— 没有配置块也会注册, 这样一个裸跑的 anetlinkd 总有东西可展示。no_sim 之后 --demo 输出 "no devices",这是预期的优雅退化。

四个加法标签(默认不在)

标签加入为什么反过来
onvifONVIF IP 摄像头(adapters/onvif)经厂商 HTTP 接口触达设备、要持有凭据
hikvision海康 ISAPI(adapters/hikvision)同上
dahua大华 CGI(adapters/dahua)同上
adap进程外适配器宿主(C4 over UDS,adap/)托管一个我们没有编译过、可能是别的语言写的、代我们触达硬件的进程

这四个名字登记在 cmd/anetlinkd/plugins.go 的 additiveTags 里, 作用与 ANet 的 DeclareOptIn 相同:让一个不含它们的构建也能说出正确的修法。 配置里写了没编进来的适配器,启动直接失败,并且两种方向给两种话:

adapter "onvif" is configured but not compiled in — rebuild with `-tags onvif`
adapter "modbus" configured but not compiled into this build (built with no_modbus?)

另有一个测试专用标签

realworld —— 七个适配器加 RTSP、Matter 的真实服务测试 (adapters/*/realworld_test.go)。它不影响任何产物,只决定哪些测试被编译。 台架起停是 test/realworld/up.sh / run.sh / down.sh。

job内容
testgofmt、go vet ./...,外加三条带标签的 vet:-tags realworld、-tags onvif,hikvision,dahua、两者合并;然后 go test ./... 与 go run ./cmd/anetlinkd --demo
plugin加法矩阵五行:onvif / hikvision / dahua / adap / 四个一起。每行 vet + test + --demo 启动
unplug减法矩阵十三行:十一个标签各一行,加 no_btmesh,no_zigbee,加十一个一起。每行 go build + --demo 启动
realworld起真 mosquitto / Modbus / HA / OPC UA / 摄像头,跑 -tags realworld
racego test -race ./...,外加 -tags onvif,hikvision,dahua 的一遍

plugin job 存在的理由与 ANet 的 optin 相同:这四个适配器不在任何默认构建里, 没有这个 job 它们在 CI 上根本不会被编译、vet 或测试。

ANetLink 侧同样只验编译与启动,不验符号数。 unplug 与 plugin 两个 job 里 没有 go tool nm。全套件中只有 ANet 仓库做符号数断言 —— 位置是 .github/workflows/ci.yml、build.sh、deploy/release/build-release.sh 三处。


十 · 发布件与标签

deploy/release/build-release.sh 每个平台出两个件,四平台(linux/darwin × amd64/arm64)共八个:

文件名标签
anet-<os>-<arch>无标签(即九个减法模块全在,不含 shell)
anet-shell-<os>-<arch>-tags shell

一行安装脚本的 --shell 拉取的是另一个文件,不是同一个二进制的一个开关:

curl -fsSL https://agentnetwork.org.cn/install.sh | sh
curl -fsSL https://agentnetwork.org.cn/install.sh | sh -s -- --shell

发布件用 -ldflags '-w' 而不是常见的 -s -w:-w 只去掉 DWARF 调试信息, -s 会连符号表一起去掉,而那样 go tool nm 会回答"没有符号" —— 读起来像检查通过,实际什么也没有证明。代价是每个二进制多约 1.4 MB,这是有意付的: 不付,判据对下游就不成立了。发布脚本自己也把这条当成失败条件, 产物没有符号表就直接报错,而不是让检查空转着通过。

在自己下载到的文件上重做判据的完整流程见 自行核对二进制。


十一 · 已知缺口

  • hub 与 ANetLink 的标签没有符号数判据。 两边的 unplug job 只验编译 (ANetLink 另验 --demo 能启动)。"去掉之后还能编译"比"代码不在二进制里"弱一档。
  • 组合矩阵不是全集。 ANet 九个减法标签共 512 种组合,CI 跑 18 种; ANetLink 十一个减法标签共 2048 种,CI 跑 13 种。没进矩阵的组合可能编译不过。
  • 没有"发行版名字"这个字段。 anet version 报模块列表,不报"这是 standard 档"。 anet version --profile 不存在:CLI 现在按 cmd/anet/main.go 的 knownFlags 表 拒绝未知 flag,这条命令答 anet version takes no flags, and --profile is not one。 发行版名字是设计稿里的待做项。 anet status 不报模块 —— 它里面的 "profile" 指的是 agent 的自述资料。
  • ANet/docs/DISTRIBUTIONS-zh.md 里的部分数字与本页不一致。 该文档正文有一处 "体积 17.6 MB" 与它自己的实测表(12,132 K)矛盾,并称"CI 跑 10 种 tag 组合" (实际 18 种)。本页数字以 .github/workflows/ci.yml 与 发行版总览 的 2026-09-03 实测为准。
  • 发行版档位的 tag 串内部不齐。 CI 矩阵里 agent 与 p2p 两档的串没有 no_taskboard,min / standard / paid 三档有;而 ANet/docs/DISTRIBUTIONS-zh.md 列的五档串一个都没有 no_taskboard, 与 CI 的前三档也对不上。这是 taskboard 加入较晚、只有一部分串跟着更新的实际差异, 不是设计意图。要不要统一,未决。
  • 符号数只回答"代码在不在",不回答"它做了什么"。 full > 0 / lean == 0 证明链接器去掉了这个包,不证明这个包的行为正确 —— 那是单元测试与 scripts/joint.sh、scripts/joint-shell.sh、scripts/scenario.sh、 scripts/prodtest.sh 几条联调路径的事。