阅读约 7 分钟
基于 CardanoWall API 构建你的产品
如何在 CardanoWall 之上构建你自己的产品:通过 CardanoWall 自身所运行的同一套网关 API,完成报价、上传、发布、读取、验证以及监控 Label 309 记录。

你可以把存在性证明(Proof of Existence)功能直接嵌进自己的产品,而不必把用户引导到 CardanoWall 的网站上。CardanoWall 只是一套托管网关之上的用户界面,而这套网关对外暴露了一组普通的 HTTP API:为一份证明报价、在需要存储时上传内容、发布一条 Label 309 记录、追踪其状态、读取公开记录、查询余额,以及接收 webhook。最终落到 Cardano 上的那份证明是标准的 Label 309 元数据,而不是某种 CardanoWall 专有的私有格式——因此日后任何人、用任何工具都能对它进行验证。
这一点至关重要。产品体验可以完全由你掌控,而证明本身始终保持独立可验证。这正是 CardanoWall 自家 Web 应用和 worker 所运行的同一套 API——既没有私有通道,也没有更快的内部捷径。
你能用它构建什么?
只要工作流中需要存在性证明,这套 API 都能派上用场:
- 为客户文档加时间戳的 SaaS 产品;
- 把发布清单锚定到链上的 CI/CD 流水线(参见 CI/CD 中的证明);
- 大规模 发布来源溯源记录 的 AI 平台;
- 每日提交证据批次的合规归档系统;
- 为特定接收方封存证据的法律工具;
- 对控制项快照进行签名的内部审计系统;
- 列出某发布方全部记录的公开区块浏览器或主页。
这套 API 不只是网站之上的便利封装,它是自动化的接入面——当流程中没有人工参与时,你要找的就是它。
主发布流程是怎样的?
一次发布分为三个阶段:「报价」、「上传」(仅在字节确实需要存储时),再到「发布」。映射到网关的数据平面(/api/v1/*)上,对应为:
POST /api/v1/poe/quote——在一个短暂窗口内锁定价格。POST /api/v1/poe/uploads——存储内容,并返回内容寻址 URI。POST /api/v1/poe/uploads/sessions及其分块路由——面向大文件的可续传路径。POST /api/v1/poe/publish——提交最终确定的记录。POST /api/v1/poe/publish-batch——一次调用提交多条记录。GET /api/v1/poe/events/{poe_id}——通过 Server-Sent Events 流式推送状态更新。
请求体与响应体的确切定义在网关的 OpenAPI 文档中,每个部署都会提供——请以它为准来对接,而不是照搬一篇博客里的片段。真正保持稳定的是这套心智模型:先锁定价格,再存储所需内容,最后把最终确定的 canonical-CBOR 记录字节连同报价 id 一起提交。
为什么报价要排在最前面?
因为发布是一项付费操作,而且价格事先无法得知。
网关代你支付真实成本:Cardano 交易费、附带内容时的 Arweave 存储费、美元与这些网络之间的汇率敞口,以及运营方的利润加价。报价会在发布调用真正花掉任何费用「之前」就把成本摊开。(关于发布为何收费的来龙去脉,参见 发布为什么要收费。)
一份报价响应包含一个 quote_id、一份按项拆分的价格明细(网络、存储与服务三部分)、所应用的利润加价、汇率快照的新鲜程度,以及一个过期时间戳。价格只在有限的窗口内锁定——大约十五分钟——之后你需要重新请求一份新报价。
这让你的应用得以判断:
- 账户余额是否足以支付这次发布;
- 汇率快照是否足够新鲜,符合你的策略;
- 此操作是否需要用户确认;
- 是否要拆分一个大批次;
- 旧报价过期后是否要带着新报价重试。
在网关确认某操作确实不花钱之前,不要显示「免费」标签。仅含哈希、不带存储的记录可能很便宜,但价格的权威是网关,而不是你的界面。
上传这一步在做什么?
上传负责处理那些需要有地方落脚的字节。
仅含哈希的证明无需上传任何内容——摘要已经握在你手里。而带有附加内容、封存密文或恢复材料的记录则需要内容寻址存储,上传端点会存下这些字节并返回一个形如 ar://... 的 URI。这套 API 支持分片上传、逐文件返回结果、在账户内按内容哈希去重,以及针对单次请求发送不完的大文件提供可续传会话。CardanoWall 不设固定的上传大小上限——内容按字节计费,数 GB 量级的上传也在预期之内。
把上传当作它自己的一段小生命周期来对待:
- 上传字节。
- 拿到内容寻址 URI。
- 用该 URI 构建或最终确定 Label 309 记录。
- 发布记录。
如果一次上传返回了一个进行中的尝试 id,请轮询该尝试的状态端点,而不要盲目重传——只因为漏看了一次响应就重新发送一个数 GB 的文件,正是这个尝试 id 要预防的那类失误。
发布做了什么,又为什么是异步的?
发布会提交记录,并启动链上流水线。
发布调用接收最终确定的 canonical-CBOR 记录字节,外加 quote_id。网关会在元数据 label 309 之下把记录锚定到 Cardano,扣除报价金额,并在交易仍处于提交与确认过程中时就返回一个网关记录 id(一个 poe_... 值)。
这种异步性对你的设计很重要。当调用返回时,链上交易哈希可能还不存在。请展示一个待定状态,并监听后续更新。API 报告的状态会在以下几个取值之间流转:
submitting——交易正在构建并广播;confirming——交易已上链,但仍低于确认阈值;confirmed——交易已越过该阈值并完成结算;failed——发布已终态失败,不会再落链。
GET /api/v1/poe/events/{poe_id} 处的状态流正是为此而存在。一旦发生终态失败,网关会自行冲销那笔扣款——这样你的产品就能诚实地告诉用户「你只为真正落链的内容付费」,而无需自己跑一套对账。不要为发布失败再搭一条厂商侧退款路径,那会导致重复退款。
你的 API 客户端应该存些什么?
存下足以重新接续工作流的信息即可——不多不少。至少包括:
- 你自己的用户或账户 id;
- 网关的
poe_id; - 本次发布所用的报价 id;
- 记录摘要,或记录字节的哈希;
- 最终的 Cardano 交易哈希(一旦它生成);
- 状态与各时间戳;
- 任何上传尝试 id;
- 任何内容寻址 URI;
- 日后验证时你需要的任何本地材料。
不要把你的数据库当作证明本身,而要把它当作产品的读模型。证明是链上的 Label 309 记录,加上验证它所需的内容或密钥——这一组合自成一体,独立于你的服务器。
你该如何读取并验证记录?
记录接口面是为公开、匿名的读取而设计的:
GET /api/v1/records——链上记录的信息流,支持过滤与分页。GET /api/v1/records/count——该信息流的计数。GET /api/v1/records/{tx_hash}——按交易哈希获取单条记录。POST /api/v1/records/{tx_hash}/verify——一份服务端验证报告。
列表与获取路由面向匿名调用方——公开验证页面和区块浏览器无需任何凭据。带上 bearer 令牌可以在相关场景下补充账户上下文,但公开记录验证绝不应依赖于某个私有的 CardanoWall 会话。
把这些端点用于产品层面的便利。对于高保障的检查,还应额外运行一个「独立验证器」,它通过自己选定的 Cardano 基础设施去拉取链上数据,并产出一份独立报告。这才是健康的分工:API 让应用更易构建,但证明仍必须在 API 之外自行成立。开源 SDK 与 CLI 内置的正是这样一个验证器——它端到端如何工作,参见 验证一条 Label 309 记录。
身份验证该怎么设计?
把凭据的权限收窄,并把运营方凭据牢牢留在你的后端。
网关把两个平面分开。你的用户在「数据平面」(/api/v1/*)上活动,使用一个由你的后端按会话签发的短时「账户令牌」或 API 密钥。而「控制平面」(/control/v1/*)的「运营方凭据」只由你的后端独自持有——用于开通账户、在你的计费收到款项时记入额度、设置利润加价,以及签发那些账户令牌。
数据平面的访问是带权限范围的。你会用到的范围有:
poe:create——报价、上传、发布以及批量发布;poe:read——记录读取、verify 路由,以及带 bearer 调用时的状态流;account:read——余额与账本读取;webhooks:read与webhooks:write——账户范围内的 webhook 管理;billing:read——预留给厂商计费界面。
一个发布页面需要 poe:create;一个余额组件需要 account:read;二者都不需要更多。运营方凭据绝不能进入浏览器打包产物、移动应用、合作方脚本,或某个只需发布证明的 CI 任务——后者应改为各自签发带权限范围的账户令牌。一个泄漏的账户令牌应当只是一个「一小时的问题」,而不是一场安全事件。
重试与幂等该怎么设计?
在投入生产之前就把重试设计好,因为发布系统的失败往往乏味而平常:网络抖动、报价过期、余额不足、限流、一次仍在进行中的上传,或一条中断的状态流。
一套扎实的集成应当:
- 在 API 支持之处使用幂等,并以一个稳定的源头 id 作为键;
- 把报价过期当作常态来处理——取一份新报价继续即可;
- 在出现不确定的失败后,去轮询权威的尝试端点或记录端点,而不要盲目重新提交;
- 避免重复上传大文件;
- 避免重复记入余额(每一笔额度都以付款自身的 id 作为键);
- 记录请求 id 与网关 id 以备支持排查;
- 在你的报告中把「失败」的发布与「不可验证」的记录区分开来。
发布会按记录字节去重,上传批次也支持幂等重放。请依靠这些语义,而不是「再试一次碰碰运气」的循环。
webhook 该承担什么?
webhook 是你构建读模型的方式——不是靠每秒轮询,更不是去读网关的数据库表,那些表属于引擎内部,会在不另行通知的情况下变更。
注册一个 webhook 订阅后,网关会推送生命周期事件:poe_status_changed、balance_changed、退款意图、上传失败。运营方可以订阅整个实例范围的事件 firehose;数据平面上同样存在账户范围的 webhook 路由。一个「已发送项」视图——每个用户看到自己发布过的记录及其实时状态——就是最典型的用途:消费 poe_status_changed,把它投影到你自己的表里,再从那里渲染。
你的接收端应当验证每次投递的签名、接受至少一次投递、以事件 id 或投递 id 作为投影的键,并把重复投递当作空操作处理。读模型归你的应用所有;而权威的花费与发布状态归网关所有——你大可缓存它用于渲染,但任何会触发花费的决策,都要去读网关。
哪些东西绝不能离开客户端?
私有的身份材料。你的应用调用 API 是为了「发布」记录;它绝不应把用户的身份种子、签名私钥或接收方私钥交给网关。
- 对于已签名记录,请在本地签名或采用主机外签名——SDK 提供了一种流程:私钥存放在 KMS、HSM 或一台气隙机器上,只有公钥和签名会经由网络传输。
- 对于封存记录,当用例需要端到端机密性时,请在上传前于本地完成加密。
- 对于接收方验证,请在本地解密。
这套 API 用于发布与生命周期操作。它不是信任根,而且其设计本就让它永远无需成为信任根。关于这一更广的原则,参见 为什么密钥永不离开设备。
从哪里开始
最快的路径是 github.com/cardanowall 上的开源 SDK 与 CLI:一个 TypeScript SDK(@cardanowall/sdk-ts)、一个 Python SDK(cardanowall-sdk)、一个 Rust SDK(cardanowall),以及 cardanowall CLI。它们都与具体网关无关——由你提供基础 URL 和一个不透明的 API 密钥——并且都内置同一个独立验证器。如果你想先用手工方式驱动 API,发布你的第一份证明 会端到端走完一条记录,而 在自动化中使用 CLI 则覆盖脚本化的路径。如果你想自己运行这套后端,而不是用托管版本,网关同样是开源的——参见 运行你自己的网关。
延伸阅读
- Label 309,本文所基于的开放标准:label309.org
- 开源 SDK、CLI 与网关:github.com/cardanowall
- 针对该元数据 label 的 Cardano CIP 提案,已提交至 Cardano CIP 流程,正由 CIP 编辑审议中:github.com/cardano-foundation/CIPs/pull/1205
- 发布你的第一份证明
- 验证一条 Label 309 记录
- 在自动化中使用 CLI
- 什么是 Label 309 网关?