AGENT NETWORK · DOCS
发行版

anet-paid

paid 档:能做什么、不能做什么、姿态如何

paid 是 standard 加上付费模块 x402。它是六档里 第一档可能对外开一个公开监听口的,也是第一档需要运维理解托管边界的。

先说一件容易搞反的事:装 paid 不是为了"获得付费能力"。一行安装拿到的默认二进制 带着全部八个减法模块,本来就带 x402。paid 的意义在减法 —— 它是"能收费,并且只能收费" 的那一档:MCP、点对点、任务板、共脑、组织凭证、内容寻址存储、设备能力,七样都不在二进制里。


一 · 给谁

要靠对外提供能力换取 credit,并且不希望这台机器带任何用不上的子系统的运维。

典型形态是一台只跑一件事的服务器:后面挂着一个自己写的 HTTP 服务,前面注册在一个 hub 上, 按调用次数收 credit。它不需要 MCP(没有 IDE 连它),不需要 p2p(买家从公网来,走 hub), 不需要设备适配(它不管硬件)。

如果你的场景是"我想在自己的 IDE 里用 anet",那是 agent 档; "我只是把服务挂上去,不打算收钱",那是 standard。


二 · 怎么编出来

tag 串

no_mcp,no_taskboard,no_cas,no_org,no_blackboard,no_p2p,no_anetlink

七个减法 tag,留下 service 与 x402 两个模块。这一串同时出现在 .github/workflows/ci.yml 的 pluggable 矩阵和 scripts/onboard.sh 的 PROFILE[paid] 里 —— 每次提交都按这一串编译、跑一遍 go test,不是文档里凑出来的。符号级核对的范围要说清楚: CI 对矩阵里的每一行只核对 tag 串里的第一个 tag(这一行是 no_mcp), 其余六个模块各有一行单 tag 的矩阵行做同样的核对。

go build -tags "no_mcp,no_taskboard,no_cas,no_org,no_blackboard,no_p2p,no_anetlink" -o anet-paid ./cmd/anet

或者用带 commit 戳的构建脚本(它会把 commit、构建时间和 tag 串一起写进二进制):

TAGS=no_mcp,no_taskboard,no_cas,no_org,no_blackboard,no_p2p,no_anetlink OUT=./anet-paid bash scripts/build.sh

编完立刻核一遍:

./anet-paid version
anet 0.1.7 (commit <短哈希>, built <UTC 时间戳>)
modules: service,x402

上面是输出的形状,不是某一次构建的转录 —— 尖括号里两处随构建而变。

第二行读的是模块注册表(module.Compiled(),init() 跑过才在里面),不是构建时的 tag 字符串, 所以它不会说谎。只有 service,x402 两个名字才是 paid。

第一行的 commit 和时间来自 -ldflags -X。直接 go build 不带这些 flag 时它们是 unknown, scripts/build.sh 会把它们戳进去 —— 版本常量只能回答"这是哪个 release", 回答不了"生产上跑的是不是我刚编的那个"。

scripts/build.sh 用 -ldflags "-s -w",-s 会剥掉符号表,go tool nm 因此答不出东西。 要在这个二进制上自己数符号(go tool nm ./anet-paid | grep -c module/x402 应大于 0、 grep -c internal/mcpserv 应等于 0),编的时候就别加 -s。 详见自己核一遍。

一行安装对应什么

没有对应写法。 发布件里那份安装脚本(ANet/deploy/release/install.sh)的全部 flag 是 --system --user --prefix DIR --base URL --hub URL --name NAME --token INVITE --shell --help —— 其中只有 --shell 会换二进制(换成 anet-shell-<平台>), 没有任何 flag 能选发行版档位。--token 是 hub 开了准入之后注册要用的邀请码,与档位无关。 线上那个 URL 实际服务的是哪一份副本,以 curl -fsSL https://agentnetwork.org.cn/install.sh | sh -s -- --help 的输出为准。

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

这条命令装到的是 anet-<os>-<arch>,即不带任何减法 tag 的 full 档(含 x402,不含 shell)。 它能收费,但它同时带着 MCP、p2p、任务板、共脑、组织、CAS、anetlink。

min / standard / paid / agent / p2p 五档目前没有发布件,只能自己编。 v0.1.7 的 deploy/release/build-release.sh 每平台只出两个件:anet-<plat>(full)与 anet-shell-<plat>(full+shell)。这是总览页 第八节记录的已知缺口之一。


三 · 配置:两个块

paid 只认两个模块配置块,都写在 ~/.anet/config.json(或 ANET_DATA_DIR 指向的目录)的 modules 下面。

modules.service —— 卖什么,多少钱

{
  "hub_url": "https://hub.agentnetwork.org.cn",
  "modules": {
    "service": {
      "capabilities": [
        {"id": "text.digest", "url": "http://127.0.0.1:29720", "description": "sha256"},
        {"id": "text.digest.paid", "url": "http://127.0.0.1:29720", "price": 25, "description": "sha256, 25 credits"}
      ],
      "timeout_ms": 120000
    }
  }
}
  • price 的单位是 hub 账本的 credit,省略或写 0 表示免费,不是"价格为零"。 免费的能力根本不产生 402,调用方不必解析一个付款要求才知道不用付钱。
  • id 与 url 缺一不可,id 不能重复,否则启动时报错(module/service/service.go 的工厂函数)。
  • timeout_ms 默认 2 分钟。一个挂住的能力会一直占着一次委派,而委派方在 hub 另一头等着。

modules.x402 —— 要不要开公开兑付口

大多数 paid 节点这个块可以完全不写。付费模块在没有配置块时照样加载(它的工厂函数 对空配置返回一个可用的模块而不是 nil),因为一个已经注册在 hub 上的节点本来就能收付款, 不需要额外配置。

只有要开凭证兑付口才写:

{
  "modules": {
    "x402": {
      "voucher_addr": "0.0.0.0:8402",
      "voucher_url": "https://your-node.example/x402/redeem",
      "witness_hub": true,
      "witness_every_seconds": 3600
    }
  }
}

voucher_addr / voucher_url 见第七节,witness_hub / witness_every_seconds 见第四节第 7 条。 这里先记一条:voucher_addr 与 voucher_url 必须同时给,只给一个会在启动时被拒绝, 错误信息是 x402: voucher_addr and voucher_url must be set together。


四 · 能做什么

standard 的全部(注册、被找到、按能力 id 对外提供服务、签发收据、上证据链),加上下面这些。

1. 收费:标价的能力答报价而不是拒绝

配好 price 之后不需要别的动作。买方第一次投递时,paid 节点回一个 PAYMENT_REQUIRED —— 这是一个完整答案:签了名、有收据、上了链,不是一个错误。

买方带上授权再投一次,paid 节点拿授权去 hub 结算,先结算后干活,然后执行、回结果。 顺序是有意的:后结算意味着干完才发现收不到钱;先结算意味着活失败了钱已经付了。 选后者是因为后者证据模型说得清楚 —— 效果和付款都在两条链上。

2. 花钱:买别人标价的能力

anet delegate <provider-aid> --capability text.digest.paid --args '{"text":"hi"}'

不带 --pay 时你拿到的就是那张报价,自己决定付不付。要一次做完:

anet delegate <provider-aid> --capability text.digest.paid --args '{"text":"hi"}' --pay

--pay 是两次委派,不是一次的续期:第一次以报价结束(那是真答案,双方链上都有), 第二次带着授权重新投。授权里写死了金额、收款方、账本和这一次交互的 id, 所以它不能被重新瞄准到别的活、更大的数或另一个 hub。

3. 看账

anet balance

回的是 hub 账本上的余额加近期流水条目。余额是 hub 的数,这一点第十节会展开。 本节点自己链上的对应事件用:

anet evidence --type anet.payment.settled

两者不一致是能看见的,这正是分开存两份的理由。

4. 对账与审计

anet reconcile

把本节点签过的授权、验过的结算、做过的兑付,与 hub 公布的这个账户的流水做比对。 报告里 missing_from_hub(本节点记了、hub 的流水解释不了的付款)是严重项; unexplained_hub_entries(hub 有、本节点没有对应事件的条目)不一定有问题 —— 运营发放本来就没有 agent 侧事件,所以它是列出来而不是判为造假。

anet audit-hub

拉取 hub 的发放链,验签、验前向哈希链接,并与本节点此前记录过的链头比对。 它阻止不了 hub 增发 —— hub 就是发行方 —— 它确立的是 hub 无法追溯地或无声地做这件事。

5. 兑付:把 credit 拿出来

anet redeem 100 --ref invoice-2026-08

签一张"付给 hub"的授权。额度真的离开流通,hub 签字说明取走了多少、对着哪个外部凭据。 --ref 对这套代码是一串不透明字符 —— 发票号、打款流水、你和运营方约好的任何东西; 它不碰外部支付,也不假装碰。收据回来后写进本节点的链,并标 verified 表示 hub 的签名验过没有。

6. 给 x402 网关签一笔付款

anet x402-authorize --pay-to <provider-aid> --amount 30

只在 stdout 打印 PAYMENT-SIGNATURE 头的值,可以直接管进 curl。它不结算任何东西 —— 签名授权的是一笔别人可以去 facilitator 兑现的付款,在兑现之前什么都没动。 可选 --network hub:<hub-aid> 与 --interaction <id>。

7. 给 hub 的发放链签见证(可选)

modules.x402.witness_hub: true 打开后,节点会按 witness_every_seconds(默认 3600 秒) 定期取 hub 的发放链头,记在自己的链上并回签一份声明给 hub。默认关 —— 精简节点不该被要求 替网络干活;打开的节点收益最直接:它拿到了关于自己余额所在账本的独立证据。


五 · 不能做什么,以及它是怎么表现的

这一节比上一节重要。"不在二进制里"和"关掉了"是两种性质,而表现形式决定了你会不会被它坑到。

MCP:命令存在,但明确拒绝

anet mcp
error: anet mcp: this build was compiled with -tags no_mcp

(error: 前缀由 cmd/anet/main.go 统一加在子命令的错误上。)

cmd/anet/mcp_stub.go 在 no_mcp 下提供这个桩,而不是让子命令消失。理由写在代码注释里: 一个在 MCP 客户端里配好了 anet mcp 的运维,应该拿到一句话说明装错了档,而不是 "unknown command"。

点对点:没有传输,但命令不拦你

paid 里没有 p2p 模块,所以没有直连投递,委派全部经 hub 中继。这是预期行为。

但要注意一个不一致:

位置:internal/daemon/hub_client.go 的 AdvertisePeerAddress,经 internal/daemon/control_api.go 的 hP2PAdvertise 暴露。 行为:anet p2p-advertise <addr> 在 paid 构建上会正常执行 —— 它只是签名并向 hub POST /agents/{aid}/p2p 发布一个地址,路径全在内核里,不检查 p2p 模块是否编进来。 影响:节点会在 hub 的地址目录里公布一个自己根本不监听的地址,对端拿去拨号只会失败。 anet help --all 里那句 "needs the p2p module" 是提示,不是拦截。 发现方式:读 p2p-advertise 的调用链,它没有经过任何模块。

要点对点见 p2p 档。

设备、共脑、组织、CAS、任务板:配了就拒绝启动,并点名 tag

这五个模块不在二进制里。如果 config.json 里留着它们的配置块,daemon 起不来, 错误信息点名缺哪个 tag:

module "anetlink" is configured but not compiled into this build (built with no_anetlink?)

来自 module/module.go 的 Build。这是有意的:一个读到 "blackboard": {...} 却默默忽略的 daemon, 会让运维对着"为什么共脑没同步"查上半天。

注意 providers.anetlink.socket 这个旧写法也算配置了 anetlink (internal/daemon/modules.go 的 moduleConfig 会把它折成 anetlink 的模块块), 所以从 full 切到 paid 时这一项同样要删。

加法 tag 的错误提示措辞不同。shell 默认不在,所以它的提示是 it needs -tags shell, which the default build does not use,而不是让人去找一个 根本不存在的 no_shell。见 module/module.go 的 optInNames。

对方没有付费模块时:你会拿到 UNAVAILABLE

这是 paid 最常遇到的"不能做"。你标好了价、也能付钱,但对面是 min 或 standard:

  • 你向一个无 x402 的提供方要一件标价的能力 → 它答 UNAVAILABLE,消息里带 built with -tags no_x402, or the x402 module is not configured。它不会免费替你干。
  • 反过来,一个无 x402 的买方向你要一件标价的能力 → 它连授权都签不出来, 它自己的 /x402-authorize /balance /redeem /reconcile /audit-hub 五个入口 全部回同一句话。

scripts/onboard.sh 第 6 节核对第一种情况,第 7 节核对第二种(它实际跑 /balance /redeem /reconcile /audit-hub 四个),第 8 节核对 paid 自己这一侧。

一个反直觉但重要的细节:standard 档也会在自己的卡片里公布价格。 internal/daemon/card.go 的 priceList 只问 provider 实现没实现 provider.Priced, 而 service 模块无论有没有 x402 都实现了它。所以一个 standard 节点可以"标了价、 公布了价、然后拒绝执行"。这不是缺陷 —— 它拒绝比白干正确 —— 但配 price 之前要确认 自己这一档带 x402。用 anet version 看第二行。

诚实效果状态:五个值不合并

状态在 paid 上什么时候出现
OK活干完了,证据随结果返回
PAYMENT_REQUIRED你要钱,并且告诉了对方多少。不是错误
UNAVAILABLE做不了。例如后端服务连不上,或对方无付费模块
FAILED试了,没成。例如后端返回 5xx,或回复不是 JSON 对象
UNVERIFIED做了,但拿不到可验证的效果信号。service 模块不产生这一个

UNAVAILABLE(没试)与 FAILED(试了没成)是两种状态,不合并。 见证据与效果。

service 模块只会答 OK / FAILED / UNAVAILABLE(module/service/service.go 的 Invoke),PAYMENT_REQUIRED 由内核在调用之前给出。UNVERIFIED 来自能对自己的效果 说得更细的 provider(例如 ANetLink 的适配器),而那些模块不在 paid 里。


六 · 姿态

监听什么

面地址默认
回环控制面127.0.0.1:39811(DefaultConfig)每一档都有,不属于任何模块
凭证兑付口modules.x402.voucher_addr默认不开。不写这个字段就没有 net.Listen

控制面只有 bearer token 保护,走明文 HTTP,所以它必须留在 127.0.0.1。 这不是 paid 特有的,是所有档共有的。

x402 是 anet 进程里唯一会 net.Listen 的模块,位置是 module/x402/voucher.go 的 startRedeemFace,只在 voucher_addr 非空时执行。 不开这个口的 paid 节点照样收费,只是买家走 hub 中继而不是直连兑付。

出站连什么

  • 它注册的那个 hub:中继轮询、结算、拉余额与流水、拉发放链(/x402/supported 的清算账本表缓存 5 分钟)。
  • modules.service 里写明的每一个 HTTP URL(通常是 127.0.0.1 上的本机服务)。
  • 打开 witness_hub 后,按小时给 hub 的发放链头签见证。

没有别的。

那七个减法 tag 各减掉了什么

no_mcp 减掉的是 CLI 的 MCP northbound(cmd/anet/mcp.go),它不是挂在 module.Host 上的模块;它由 cmd/anet/module_mcp_declare.go(同一个 //go:build !no_mcp)声明进 anet version 的模块行,所以 paid 这一行没有 mcp,正说明这个 tag 生效了。其余六个是模块。

减法 tag少了什么
no_p2p这台机器不再需要跑 anetpeer,因此不接受入站对等连接
no_mcp少 34 个依赖包(MCP Go SDK 及其 jsonschema 依赖树)
no_anetlink不连 ANetLink 运行时的 C1 unix socket
no_taskboard不会去 hub 的共享任务板领活
no_cas / no_blackboard / no_org这三个包里没有任何 net/http 或 net.Dial 调用,拔掉减的是代码量不是网络面

shell 在 paid 里也没有 —— 它是加法 tag,默认构建从来没有它,full 也没有。 要带就是另一个二进制:-tags shell,no_mcp,no_taskboard,…,见 +shell。


七 · 那个公开口

只有 paid(和 full)才有这一节。

它是干什么的

hub 可以替一个 agent 卖能力:买家在 hub 上付钱,hub 结算并交给买家一张凭证, 而不是结果。买家拿凭证直接去 agent 兑付。

GET  https://hub.example/x402/resource/<aid>/<capability>   → 402 + 价钱 + redeem_at
     (带上 PAYMENT-SIGNATURE 头再来一次)                    → 200 + 凭证
POST <redeem_at>   {"voucher":"…","capability":"…","args":{…}}   → 结果

redeem_at 就是这台机器上 voucher_url 的值。hub 只卖门票,不代理内容 —— 请求和结果都不经过它,它搬不动自己读不懂的字节。

hub 在这条路上不能做三件事

  • 不能定价。价钱读自 agent 自己签的卡片(anet.pricing 扩展),hub 拿不出那个签名, 最多拒卖。
  • 不能指路。兑付地址同样在卡片的签名里(endpoint 协议名 x402-redeem), 否则 hub 就能把买家指到自己的机器上,那就又变成代理了。
  • 不能替你查重。一张凭证只用一次,而这道关卡在 agent 这一侧。

一次性怎么保证

module/x402/voucher.go 的 spent set 从本节点自己的证据链上重放 (事件类型 anet.voucher.redeemed,启动时最多回读 10000 条)。 只放在内存里会让进程一重启就忘光,持票人就能同一份钱换两次活。 记录写在干活之前:崩在中途的买家有真实的申诉和证据,反过来则让崩溃可以被刷成免费劳动力。

拒绝也记(anet.voucher.refused)。不记的话,一个付了钱被拒的买家手里只有这台机器的沉默。

开这个口之前要想清楚的

voucher_addr 与 voucher_url 分开,是因为它们是两件事。 监听地址常写成 0.0.0.0:8402 或容器内端口;把这种地址签进节点卡片等于对外广告一个 谁也到不了的门牌。世界看到的是什么,只有运维知道 —— 所以要你自己写。 只给一个字段会在启动时被拒,而不是安静地卖出收不到的货。

  • NAT 后面没有入口的机器不要配这个口。 hub 会因为卡片里没有兑付地址而拒绝出售 (409,并提示走中继委派),而不是卖一张兑不掉的票。这种情况用普通的付费委派就行。
  • 这个口是 public 的,和回环控制面姿态相反,所以它的 HTTP 处理是另写一份的 (module/x402/http.go),请求体上限 8 MB。共用工具函数迟早意味着共用默认值, 而适合一扇私门的默认值对一个开着的端口是错的。
  • 兑付一次的超时是 10 分钟。买家已经付过钱了,为了让 socket 好看而截断工作等于收钱卖超时。

八 · 实测数字

数字来自总览页第五节的重测(2026-09-03,go 1.26.7, linux/amd64,CGO_ENABLED=0,-trimpath -ldflags "-s -w")。

档体积依赖包数anet version 报的模块
min12,100 K265(none)
standard12,124 K266service
paid12,240 K267service,x402
paid+shell12,292 K268service,shell,x402
full14,320 K308八个减法模块全在

三条读法:

  • standard → paid 只多 116 K 和 1 个依赖。 这两档的区别是"不可能付款"与 "能收费并可能开公开口",不是 116 K。分档的理由从来不是体积。
  • min → paid 只多 140 K 和 2 个依赖。
  • paid 比 full 小 2,080 K,其中绝大部分(约 1,900 K)是 no_mcp 省下来的。 剩下六个 tag 加起来不到 200 K。

ANet/docs/DISTRIBUTIONS-zh.md 的同一行记的是 12,264 K / 267 个依赖,与上表差 24 K (min 与 standard 两行也各差二三十 K,full 的依赖数差 1)。上表标注了测量日期与工具链, 那份文档的实测表没有标注日期,所以连两次测量隔了多远都说不出来,差异原因未核实。 要以自己能复现的数字为准,按自己核一遍重跑一次。


九 · 升级与切换

身份和数据不受影响

身份私钥、config.json、交互记录和证据链都在数据目录里 (默认 ~/.anet,可用 ANET_DATA_DIR 改),不在二进制里。换二进制不动这些。

具体做法:

停掉节点。 anet stop(或 anet stop --all)。它走控制面优雅关闭,不需要找 PID。

换二进制。 把新编的 anet-paid 放到原来那个路径上(--system 装的一般是 /usr/local/bin/anet)。

先清配置,再启动。 新档里没有的模块,配置块必须先删,否则 daemon 拒绝启动并点名 tag (见第五节)。

启动并核对。 anet up,然后 anet version 看模块行、anet status 看 hub 注册, anet logs 看启动那行 anet: modules compiled in: …。

从哪一档切过来要改什么

从要做的事
standard → paid配置不用动。给要收费的能力加 price 即可
full → paid删掉 modules 下的 anetlink / blackboard / org / cas / taskboard / p2p 块,以及旧写法 providers.anetlink;如果 IDE 里配了 anet mcp,那条也要撤
paid → standard删掉 modules.x402 块。留着会拒绝启动;删了之后标价的能力开始答 UNAVAILABLE

降档不会撤回已经公布出去的东西。 从 paid 切到 standard 之后:卡片里的价格还在 (service 模块自己实现 provider.Priced,与 x402 无关),买家看得到价、付不了钱; 如果之前配过 voucher_url,hub 上那份卡片仍然记着一个兑付地址,而新构建根本不开那个口。 重新 anet hub-register <hub-url> 一次让卡片重签,或先把 price 去掉。

账户里的 credit 呢

余额在 hub 的账本上,跟着 AID 走,与二进制无关。但降到无 x402 的档之后你读不到它, 也取不出来 —— /x402-authorize /balance /redeem /reconcile /audit-hub 五个付费入口都会回 this build has no payment support。 要把 credit 取出来,先在还带 x402 的构建上 anet redeem。


十 · paid 特有的注意事项

一、余额在 hub 手里,这一点无法回避

credit 记在 hub 的库里。hub 可以增发,可以在你不知情时改一个数字,可以收了钱不给你兑付。 协议阻止不了这些,代码也阻止不了。

它能做到的是另一件事:hub 改不掉双方手里已经签好的记录。所以一笔账对不上时, 这是一场双方都拿得出记录的争执,而不是一方的说法。你注册到哪个 hub,就是选择信任谁。

节点这一侧可以自己核的两件事:

anet reconcile     # 本节点的记录 vs hub 说的这个账户
anet audit-hub     # hub 的发放链:验签、验链接、比对此前记过的链头

以及 hub 公开的供应量等式(任何人都能重算,不需要是这个 hub 的用户):

GET /x402/supply          已发行 / 已兑付 / 未清偿,以及链上合计与账表合计是否一致
GET /x402/issuance        整条发放链,每条带可独立验签的记录
GET /x402/issuance/head   当前链头
GET /x402/witnesses       别人对这个 hub 签过的见证

二、credit 怎么进来

只有两条路:

  • 注册赠额:首次注册到一个 hub 时自动发 100 credit(ANetHub 的 RegistrationGrant)。只发一次,按 agent 行去重,重新注册不会再发。

  • 运营发放:hub 运维在持有账本的那台机器上执行

    anet-hub -data <数据目录> -grant <aid> -amount 500 -reason "为什么"

    这是一个命令行 flag,不是 HTTP 端点。谁可以创造 credit 是一个策略问题, 这一轮没有回答,而一个端点会顺手替它回答 —— 回答得还很糟,因为它需要的那种鉴权还不存在。 要求对持有账本的机器有 shell 访问,是一个可以辩护的过渡方案:那和数据库文件是同一条信任边界。

    这条命令要求 hub 停着跑(flag 的帮助文本这么写)。发放会记进 hub 自己那一行的供给计数, 所以它照常出现在 /x402/supply 里 —— 账外造币会毁掉唯一那条让托管可核验的算术。

100 credit 很小是有意的:够试出这东西有没有用,不够到值得批量注册身份来薅 —— 而身份是免费铸的,所以赠额永远不能超过铸一个身份的成本。

三、跨 hub 付款:一次付款只造一份 credit

买方在 hub A、卖方在 hub B 时,402 会同时列出 A 愿意清算的账本 (自己的在前,后面跟 /x402/supported 报出的对端账本),买方挑自己有余额的那条。

付款方 hub 在 payee 不在本地时销毁 credit 并在 hub_due 记下欠款; 收款方 hub 凭收据创造 credit。两侧各自把这次变动写进自己的发放链。 这条不是理论上的整齐 —— 曾经两侧各记一次贷,一次付款在两个账本上各造一份, 联邦总量按付款额增长,而任何一侧的单边供给检查都看不到它。

运维面在 hub 上。列出对外负债:

anet-hub -data <数据目录> -due

签一份清偿声明并投给对端(-payee 取自 -due 的输出,指明清的是哪一笔):

anet-hub -data <数据目录> -clear <peer-aid> -amount <n> -payee <aid> -peer-endpoint <url>

减记发生在投递之后 —— 对端拒收的清偿仍然欠着,先减会让两个 hub 对同一笔债务的看法不一致。

引入的信任要说明白:B 给自己用户入账,是因为 A 说它扣了自己用户的钱。 收据让这个说法可追责,但不能让它变真。所以 peer 是白名单,欠款是记着而不是抹平的。

四、出错时读什么

状态 / 字段含义
PAYMENT_REQUIRED不是错误。对方要钱,并告诉了你多少
insufficient_funds余额不够。活本身没问题
network_mismatch授权签的是另一个 hub 的账本
anet.payment.settled 的 verified: false记了结算,但没能验证 hub 的签名。"不知道"和"知道没问题"是两种状态。结果里的 paid 对象只有 transaction / amount / network / receipt,这个字段在链上那条记录里
凭证 already been redeemed这张票用过了。一次性由提供方把关
x402: voucher_addr and voucher_url must be set together启动被拒。两个字段要么都给,要么都不给
this build has no payment support这不是 paid 档。anet version 看第二行

十一 · 这一档的行为承诺是怎么验的

scripts/onboard.sh 会构建 min / standard / paid 三档并逐条核对本页的说法: paid 起得来、注册得上、读得到余额(注册赠额)、向一个无付费模块的提供方要价时 拿到明确答复而不是白干(脚本接受 UNAVAILABLE 或 PAYMENT_REQUIRED,不接受第三种)。

真实网络上的完整闭环在 scripts/prodtest.sh:两个 hub、三个节点,第 6 节走 "报价 → 只问价不扣钱 → 付款 → 活真的干了 → 收据回来了 → 双方余额各动 25 → 付款方链上有 authorized 与 settled、收款方链上有 settled";第 7 节走网关路径 —— 在一个 hub 上付钱、拿凭证、 到另一台机器的 voucher_url 上兑付。

onboard.sh 只覆盖 min / standard / paid 三档。agent / p2p / full 只有 CI 的编译与单元测试覆盖,没有端到端的新用户路径核对。这是总览页 记录的已知缺口之一。