关于 sdcreceipt
sdcreceipt 是用于验证和结算 VSL Settlement Receipt 的轻量客户端,支持离线签名检查和受控授权,并将密钥与收据分开提供。
主要功能
- 01离线验证收据
- 02创建并签署结算
- 03设置参与方密钥
- 04检查发行方和参与方签名
- 05可选检查载荷与治理收据
适用场景
- 01无需联系发行方验证收据
- 02以参与方身份授权结算
- 03检查载荷或治理绑定
- 04将验证集成到 Shell 脚本
MCP 相关
其 MCP 服务提供 `verify_receipt`、`sign_trigger` 和 `settle`。`settle` 需要在启动时指定固定端点和令牌;签名密钥也在启动时提供。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
1收录的工具列表 · 更新于 3 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| receipt | object | 是 | 要验证的 Settlement Receipt 文档。 |
| party_keys | object | 否 | 可选。用于验证触发条件的参与方密钥文档。省略后将跳过触发条件检查;如果未持有这些密钥,这样做是合理的,结果中会注明。 |
| issuer_keys | object | 是 | 发行方以 JSON 发布的密钥文档。格式可以是包含 `keys` 数组的密钥集,也可以是 did:web DID 文档。 |
| governance_receipt | object | 否 | 可选。治理 Receipt(如有)。 |
详细介绍
sdcreceipt
验证并结算 VSL Settlement Receipts。
验证无需账户、网络,也无需发行方提供任何内容。 这正是该工具的意义:声称任何人都能独立核验 Receipt,其价值恰恰取决于是否真的能做到这一点。
Apache-2.0。
pip install sdcreceipt
四个动词
# Check a Receipt you were sent. Offline.
sdcreceipt verify receipt.json --issuer-keys issuer-keys.json --party-keys party-keys.json
# Set yourself up as a party: keypair + the document to publish.
sdcreceipt init --key-id https://vendor.example/.well-known/vsl-key.json
# Authorize a settlement you are a party to.
sdcreceipt trigger receipt.json --key vsl-party.pem \
--key-id https://vendor.example/.well-known/vsl-key.json
# Ask an issuer for a Receipt. The one verb that needs an account.
sdcreceipt settle payload.xml \
--party https://vendor.example/.well-known/vsl-key.json \
--party did:web:partner.example
settle 会提示你提供所需但未给出的任何信息,current_state 除外;它默认使用载荷自身的 <current-state>。它会将 Receipt 写入 --out(或标准输出),以供 verify 和 trigger 使用;--response FILE 还会保存发行方的完整答复(治理决定和费用)。DENY 会被签发并签名,但不接受任何触发操作,settle 会说明这一点。其中有两个字段无法猜出,也从来无法猜出:发行方会对发布条件进行哈希处理,且不会存储该条件;current_state/target_state 则来自发行方持有的架构中定义的治理工作流,而不是你持有的架构。
如果发行方拒绝该状态转换,它会答复可接受的状态转换,settle 会将其打印出来:
Governance evaluation produced no Receipt. Decision was DENY
From 'draft' you can go to: review
This model's workflow:
standard: draft -> review -> published
遇到这种情况时,它会以 2 退出;发生实际故障时则以 1 退出,因此脚本可以区分“换种方式重试”和“出了问题”。
verify 只有在所有检查都通过时才会以 0 退出,因此可以在 Shell 中组合使用,无需任何人解析输出。
这就是全部功能。这是一个客户端,而不是产品;它的价值在于足够精简,便于阅读。
无需阅读任何内容,立即试用
python examples/settle.py
一次端到端结算:两个参与方、两个签名,离线完成验证。无需账户、网络或 API 密钥。参见 examples/。
面向 Agent
四个动词中有三个通过 MCP 提供,且位于同一个包中:
sdcreceipt-mcp --key vsl-party.pem \
--key-id https://vendor.example/.well-known/vsl-key.json
提供 verify_receipt、sign_trigger 和 settle。只有在服务具备所需条件时才会公布对应工具,因此不会提供每次调用都会失败的工具。
有两项刻意不提供的功能;这是安全策略,而非疏忽(另见 SECURITY.md):
- 不生成密钥。 在 Agent 会话中生成的私钥,其保管方式并不明确。
init始终由人在终端中执行。 - 不提交。
sign_trigger会返回一个由你提交的已签名触发项。提交需要调用方提供目标地址,而已签名触发项加任意主机这一组合值得拒绝。
服务在启动时使用 --endpoint 和令牌启动后,才会提供 settle;这是唯一会访问网络的工具。端点和密钥一样,在启动时固定,因此调用方无法重定向载荷的发送位置。没有任何工具接受 URL;如果将来要增加其他工具,也必须保持这一不变量。
签名密钥在启动时提供,绝不会出现在工具调用中,因此不会经过对话传递。
如果你收到一份 Receipt
你不需要在任何地方拥有账户。你需要签发方发布的密钥文档;对于已结算的 Receipt,还需要每一方发布的密钥文档。Receipt 使用各方发布密钥的 URI 标识各方;签发方的密钥文档位于固定的知名路径。
pip install sdcreceipt
curl -o receipt.json https://sdcstudio.axius-sdc.com/api/v1/vsl/receipt/<receipt id> # or the file you were sent
curl -O https://sdcstudio.axius-sdc.com/.well-known/sdcstudio-signing-keys.json
curl -o vendor-keys.json https://vendor.example/.well-known/vsl-key.json
curl -o partner-keys.json https://partner.example/.well-known/did.json
sdcreceipt verify receipt.json --issuer-keys sdcstudio-signing-keys.json \
--party-keys vendor-keys.json --party-keys partner-keys.json
签发方的文档用于验证签发方的签名;每一方的文档只用于验证该方的触发记录。
角色来自你的命令行,而非 Receipt;如果两个文档声明了相同的 key_id,则会拒绝。
自 4.3.3 起,旧的 --keys 标志会被拒绝:它会汇集所有文档,导致某一方自己的文档可以提供签发方密钥(GHSA-gwhr-82j5-883w)。
检查完成后,verify 会列出 VERIFIED 未涵盖的内容:触发记录的时间戳未签名,密钥有效期未与 Receipt 的时间进行比较,链路(previous_hash)不会被跟踪,且只有在你传入负载和治理 Receipt 时才会检查它们。
每项检查都会报告,而不仅仅是第一项失败的检查:
PASS receipt_hash: matches the canonical content
PASS signature[sdcstudio-signing-key-v1]: verifies over receipt_hash (ES256)
PASS signature[sdcstudio-pq-signing-key-v1]: verifies over receipt_hash (ML-DSA-65)
PASS signatures.algorithms: carries every signature version 1.1 requires (ES256, ML-DSA-65)
PASS trigger[https://vendor.example/.well-known/vsl-key.json]: verifies over {condition_hash, receipt_id}
PASS trigger[did:web:partner.example]: verifies over {condition_hash, receipt_id}
PASS triggers.unique: one trigger per party
PASS settlement.complete: every listed party has triggered
VERIFIED
如果只有签发方的文档,则会检查签发方签名和哈希,并将 settlement.complete 记录为尚未确立,因此 Receipt 无法通过验证:每一方都已触发这一点涉及授权声明,而没有检查任何触发签名。这是正确的结果,并非缺陷。还需传入 --schema settlement-receipt-1.0.schema.json(或与 Receipt 的 version 匹配的 -1.1),以先检查结构(pip install 'sdcreceipt[schema]')。
Receipt 1.1(自 4.3.0 起)是在 1.0 的基础上,由签发方对同一个 32 字节摘要签署两次:一次是 ES256 签名,另一次是 ML-DSA-65(FIPS 204,后量子)签名。每个列出的签名都必须通过验证,且标明 1.1 的 Receipt 必须同时包含两者;1.0 Receipt 的验证方式则与以往完全相同。签发方的密钥文档会列出这两把密钥,并为每把密钥列出其 alg。ML-DSA-65 验证功能来自 cryptography 48 或更高版本,这是此工具的必需版本。
Receipt 只携带哈希承诺,绝不携带负载。因此,验证可以告诉你一次符合规范、经过授权且由双方触发的交换已经发生,而无需任何人披露交换内容。如果你还持有负载或治理 Receipt,也可以传入它们进行检查:
sdcreceipt verify receipt.json --issuer-keys issuer-keys.json --party-keys party-keys.json \
--payload manifest.xml --governance governance-receipt.json
如果你需要成为其中一方
结算会通过你控制的 key_id 标识每一方。这是有意为之:这样验证你的签名就无需经过签发方,因此无需任何人持续在线,你过去的签名也能继续具有意义。
sdcreceipt init --key-id https://vendor.example/.well-known/vsl-key.json
这会写入私钥(模式 0600)和密钥文档,并告知你该文档必须可通过哪个确切 URL 访问。发布一个 JSON 文件就是全部的接入要求。
之后,当你收到一份需要授权的 Receipt 时:
sdcreceipt trigger receipt.json --key vsl-party.pem --key-id <your key_id>
它会输出一条已签名的触发记录,然后停止。签名本身不会产生实际操作;提交则会产生副作用,因此你需要自行发送,或添加 --submit <url>。
★ 切勿从文档中获取验证密钥
接受哪些密钥在你读取文档之前就已确定, 文档不能改变这一点。
XML-Signature 中的 ds:RetrievalMethod、JOSE 中的 jku 和 x5u,以及其他地方的等效字段,都在说*“我的密钥在这里。”* 这个指针由文档的生成者写入,因此跟随它就等于让文档指定裁定自身的密钥。伪造的文档会指定伪造者的密钥,验证便会“成功”。
这种失误悄无声息。指针可能指向一个在文档签署时有效、后来却已失效的域名;任何注册该域名的人都能在该路径提供密钥,而文档看起来毫无问题——因为文档本身确实没有问题。验证者被告知去哪里查找,也照做了。
这个工具不会犯这种错误:verify 将密钥作为参数接收,没有任何代码路径会去获取密钥。这是一项安全属性,而非不便之处。
参与方密钥也是如此,但这属于附带说明,而非例外。这些 key_id 确实是由对方控制的 URI,这是有意为之。只对你信任的 Receipt 中已有记录的标识符通过 HTTPS 解析,并保存副本——参与方日后失去某个域名,不得因此改变其旧签名的含义。
参与方密钥也必须提供。它们之所以是可选的,是因为无需它们也能检查颁发者签名和负载哈希;但这不代表缺少它们时 Receipt 已结算就有任何意义。每个参与方是否都已触发,是一项关于授权的声明,因此若没有密钥可用于核验签名,settlement.complete 会被记录为尚未确立,而非通过,Receipt 也无法通过验证。在 4.2.1 之前,只比较 key_id 字符串就会通过验证,并将伪造的触发报告为 VERIFIED。
一致性
tests/conformance/ 提供发行方发布的测试向量,测试套件会使用这些向量验证此实现。
共 16 个测试向量,其中 11 个针对 Receipt 1.0,5 个针对 1.1(每个向量都使用其自身 version 指定的架构进行检查)。每个无效向量都对应一个实际出现过的缺陷,而非假设情形:ES256 要求使用 P1363,却使用了 DER 签名;对 receipt_hash 的十六进制文本而非原始字节进行签名;治理绑定与持有的证据不匹配;从另一个具有相同发布条件的 Receipt 重放触发器,等等。因错误原因而失败不算通过——清单明确指出必须触发哪项检查。
pytest
坦诚说明独立性。 通过这些测试向量,说明此工具符合发行方发布的预期,且从未偏离这些预期。它并非独立推导的结果:此实现与发行方有着共同的设计和历史。这些测试向量对仅依据规范编写验证器的人最有价值,这也是发布它们的目的。
底层的规范化则是另一回事,并且已通过独立实现进行验证。 Receipt 中的每个哈希都基于 RFC 8785 规范化字节计算,因此,其他验证的基础是各方对规范化结果达成一致。2026 年 9 月,我们与 MTCP 项目(Ahmad Abby)进行了双向验证。该项目使用 Trail of Bits 的 rfc8785 而非 sdcgovernance 实现 RFC 8785:
- 使用
sdcgovernance.jcs验证了 MTCP 发布的 5 个测试向量。所有向量的规范字符串和 SHA-256 均逐字节一致。这组测试向量于 2026 年 9 月 9 日发布,是一组经过整理后重新生成的向量,其中使用了真实架构中的合成值:audit_record、evaluation_summary、score_record、sensor_calibration和arabic_content。它取代了 9 月 1 日发布的 4 个测试向量;承载规范形式的字段也从canonical_hex重命名为output,因此,针对早期测试向量编写的运行器需要更新。 - 使用 MTCP 的实现验证了
sdcgovernance/test-vectors/rfc8785-canonicalization.json中的 21 个测试向量。没有任何不一致,包括边界测试集:负零、次正规数、从定点到指数表示的 1e21 边界、以小写\u00xx表示的 C0 控制字符、不转义的实线号、增补平面字符,以及跨越 UTF-16 代理对的键排序。 - MTCP 发布了一次一致性测试结果,使用 cyberphone 附录 G 参考测试向量验证了19 项纯 RFC 8785 测试:6 个官方测试向量、十六进制验证、10 项硬错误拒绝测试(NaN、无穷大、超出 2^53 的整数、非 JSON 类型)、一项数字格式化测试、一项整数边界测试,以及一项针对 2011 种 IEEE-754 模式的 Node.js 差分测试,结果为零处不一致。
双方都独立进行了 Node.js 差分测试,因此数字格式化结果经过了 JS 引擎验证,而不是重新实现。这是因为 RFC 8785 §3.2.2.3 原样采用 ECMAScript Number::toString,所以对于实际容易出错的部分,应以该引擎为参照。两个语言中的三个实现,没有任何不一致。这并非任何一方项目都能单独对自身做出的声明,也正是它比同一技术脉络中的第二种意见更值得采用的原因。
这并不意味着上述 Receipt 测试向量是独立的。这意味着,当它们与你的实现不一致时,分歧在于 Receipt 规则,而非底层字节。
构建你自己的实现
你不必使用此工具,而且规范也不依赖它。 如果你要编写验证器:
不要再编写一个规范化器。 使用 sdcgovernance 或其他符合 RFC 8785 的实现。两个实现可能产生不一致,而且这种不一致不会显现出来:字节不同,哈希也不同,产物看起来就像被篡改了,而不是编码错误。此工具重新导出 sdcgovernance.jcs 正是出于这个原因,不添加任何其他内容。
无法确定时应拒绝,而不是猜测。 超出 ±(2⁵³−1) 的整数无法通过 IEEE 754 双精度浮点数往返保真,NaN/Infinity 也没有 JSON 表示形式。为这些值生成内容会产生看似正常的哈希,却只会在其他人的验证器中验证失败。
报告每项检查。 在首次失败时停止,会掩盖最重要的情况:Receipt 的签名验证通过,但治理绑定未通过。
检查曲线。 根据意外曲线上的密钥验证签名,并不能证明是你认为的那个参与方进行了签名。
版本管理
MAJOR 对应 SDC 参考模型,因此 4.x.x 版本面向 SDC4。版本号从 4 开始,而不是从 0 开始,是因为这一约定,并非此前已有三个版本。MINOR 表示功能,PATCH 表示修复。SDC5 参考模型会对应 5.x.x。
sdcvalidator、sdcgovernance 及同系列的其他项目也采用相同的方案,因此无需查找其他信息,就可以通过版本号了解产物面向哪个参考模型。
请注意,Receipt 格式版本是独立的:Receipt 中的 "version": "1.0" 或 "1.1" 表示线格式版本,而非此软件包的版本。4.3.0 是首个支持验证 1.1 的版本;更早的版本会根据版本号拒绝它,而不是猜测,这是格式要求。
依赖项
Python 3.10 或更高版本。
有两个直接依赖项,均为兼容 Apache-2.0 的库:
sdcgovernance(固定为 >=4.2.0,<5),用于 RFC 8785 规范化;以及用于 ECDSA 的 cryptography。jsonschema 是可选依赖项,仅在使用 --schema 时需要。执行一次干净的 pip install sdcreceipt 会解析这两个依赖项及其各自的依赖项,总计约 14 个软件包;pip install --report 会列出适用于你平台的确切软件包集合。
这些是库依赖项。此处没有任何内容会调用服务,verify 完全不会发出网络请求。settle 和 trigger --submit 会拒绝任何非 https:// 端点,但本地发行方的回环地址除外。
安全
请参阅 SECURITY.md,了解如何报告漏洞、支持的版本,以及此工具提供和不提供的保护。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。