Skip to content

TapeAPI 1.5.0

Choose a tag to compare

@BruceLanLan BruceLanLan released this 02 Oct 18:17
· 10 commits to main since this release

中文

TapeAPI 1.5.0:在 1.x 基础上只做新增,按 1.0 文档写的代码无需修改。TAP-10 一致模式依然是实验性的,默认关闭;1.5 把它补全到解析与消息两条路径。

安全修复(建议所有用流式 AI 回执核验的用户升级)

  • 流式 AI 回执核验可被绕过(1.0.0–1.4.0):ai.createVerifyingFetch 和 tapeapi-verify 会让一个被截断或被注入内容的 OpenAI Chat / Responses 流显示为“已核验”。需要的条件是:数据路径上有人能改字节(代理、CDN,或服务方自己),并且把结束标记和后面的内容放在同一个网络分块里;另有两种流形态(行首 U+FEFF 的行、流结束时没写完的事件)会让官方 SDK 渲染回执没有覆盖的内容。1.5.0 已全部改为失败关闭;严格模式在结束点切断流,离线核验和核验页面也会报告这两种形态。回执哈希(TAPI-21 §3.5)和测试向量不变,规范只在 §8 增加了一条说明。详见 CHANGELOG 的 Security。
  • 用量核验不再被回执里的一句话关掉:核验方以前无条件信任回执里的 usageInjected 标记,拿到完整回答也不比对用量;现在非流式回答一律比对,流式只在请求本身没要用量且格式确实会注入时才认这个标记,站点核验页也补上了用量比对,并拒绝“把整段 JSON 冒充成注入流”。
  • MCP 工具描述里的隐藏指令:不可见字符检查以前漏掉变体选择符、蒙古文自由变体选择符等;现在按 Unicode 的 Default_Ignorable 属性一并拒绝(emoji 后的单个变体选择符仍允许,常见的中日韩与 emoji 描述不受影响)。代价:已经钉住、含日文异体字序列的工具集会被拒,需要改后重新发布。
  • 其余加固:路径里的 %2F、id 里的孤立代理项、单个 JSON-RPC 请求核对回答 id、上游 isError 非布尔、限流键的 IP 规范化等,详见 CHANGELOG。X Layer 只有两家运营方,一家宕机或作恶会把钉块拖到旧块上,指南里已写明并建议加第三家节点。
  • 代价:极少数诚实流也会被拒(例如上游在事件中途断开)。
  • 感谢独立审查发现这些问题。

新增

  • 一致模式的无链输入(allChains: true):容器地址或“处理器合约#ID”在 BNB Smart Chain、Base、X Layer 上各自钉块解析;多条链命中得到 ambiguous(附候选),某条链读不到就报那条链的状态,而不是误报 not-tapeout。它会向 Base 和 X Layer 的节点发请求,所以单独开关,默认不开;不开时绝不访问别的链的公共节点。
  • 容器地址反查处理器号:SDK 随版本带每条链的处理器表快照(BSC 1,174、X Layer 263、Base 101 个),命中后必须在钉块上用 cpuAt 读回核实;没命中就分页续扫(每页 8 个、每次最多 256 个),冷启动不会向节点发上千个请求。因此容器地址现在能得到链上名字和完整的激活检查。新增脚本 node scripts/gen-processors-snapshot.mjs 可复查快照。
  • 消息与通道路径(一致模式):tapeSendKey、channelKeys 按 TAP-10 读取:每次新钉一块、每个读取都用 strict 共识并检查 eth_chainId;判定 hub-changed、circuits-changed(TAP-10 §13.8)和公钥检查(§14.4)。未激活的容器,resolve 报 unpaid,它的通道记录和 TapeSend 密钥照常可读(§12.2)。新增可选的 sealStatusStore,让封存状态跨重启保留。tapesend 的一致模式限制 chainId 不超过 2^53−1。
  • 解析路径的授权读取用 strict 共识(TAP-11 §2.2):持有人 ownerOf、合约钱包持有人的 eth_getCode 与 isValidSignature(委托与 contentSig)要求足够多的运营方都作答且一致,eth_chainId 检查也是 strict。代价要说清楚:默认节点下,BSC 有 3 家运营方,一个节点宕机或落后于钉块,一致模式的 resolve 就是 unavailable;Base 有 4 家(需要 3 家),X Layer 有 2 家(与以前相同)。siteStatus 和默认模式不受影响。
  • tap10-gap 在一致模式并开启 allChains 时,23 个可离线判断的 TAP-10 测试用例全部一致(1.4 是 18 个)。

变更

  • siteStatus(任何模式):容器地址与“处理器合约#ID”现在能得到名字和处理器号,冷启动多读几次 cpuCount、cpuAt;默认模式的 resolve 与 chain.* 不变,我们用差分测试逐请求验证过。
  • 一致模式下,不开 allChains 时“处理器合约#ID”字符串直接 unsupported(TAP-10 §4.1 只在恰好一条链命中时才解析它);对象形式 { circuits, tokenId } 不受影响。一致模式现在只接受 TAP-10 列出的 hub、factory 和 opener。
  • TAPI-26 勘误:经 TapeSend 发送的邀请只封给收件方在 hub 上可用的 TAP-10 密钥,不封给通道密钥;此前没有任何实现发送或接收过这种邀请。

你会注意到的变化

  • 我们自己的两个服务 11.1013.tape 和 12.1013.tape 已在 2026-10-01 由持有人激活(付到 2036-08-09),在一致模式下都能解析。

安装

npm install https://github.com/BruceLanLan/tapeapi/releases/download/v1.5.0/tapeapi-sdk-1.5.0.tgz

如果要单独安装 server,先装同一版本的 SDK tgz。完整变更见 CHANGELOG。本项目未经第三方审计。


English

TapeAPI 1.5.0 is additive over 1.x: code written against the 1.0 docs keeps working unchanged. The TAP-10 conformance mode is still experimental and off by default; 1.5 completes it for both the resolution and the messaging path.

Security (users of streamed AI receipt verification should upgrade)

  • Streamed AI receipt verification could be bypassed (1.0.0–1.4.0): ai.createVerifyingFetch and tapeapi-verify could report a truncated or content-injected OpenAI Chat / Responses stream as verified. It needs a party on the data path who can change bytes (a proxy, a CDN, or the service itself) and puts the end marker and the content after it in one network chunk; two further stream shapes (a line led by U+FEFF, and an event left unfinished when the stream ends) could make the official SDKs render content the receipt does not cover. 1.5.0 fails closed on all of them; strict mode cuts the stream at its end, and the offline check and the verification page report both shapes. The receipt hash (TAPI-21 §3.5) and the test vectors are unchanged; the spec only gains an informative note in §8. Details in the CHANGELOG under Security.
  • Usage verification can no longer be switched off by one line in a receipt: the verifier used to trust the receipt's usageInjected flag unconditionally and skip the usage comparison even with the full answer in hand; now a whole answer is always compared, a stream only when the request itself did not ask for usage and the format really injects it, the verification page gained the usage comparison, and it refuses a whole JSON answer dressed up as an injected stream.
  • Hidden instructions in MCP tool descriptions: the invisible-character check missed variation selectors, Mongolian free variation selectors and more; it now rejects everything with the Unicode Default_Ignorable property (a single variation selector after an emoji is still allowed, and ordinary CJK and emoji descriptions are unaffected). Cost: an already pinned tool set containing Japanese ideographic variation sequences is refused and has to be changed and republished.
  • Other hardening: %2F in paths, lone surrogates in id, the answer id checked for a single JSON-RPC request, a non-boolean upstream isError, rate-limit keys with canonical IPs, and more in the CHANGELOG. X Layer has two operators by default, so one that is down or hostile can hold the pinned block back; the guide says so and suggests a third node.
  • Cost: a few honest streams are refused too (for example an upstream that disconnects in the middle of an event).
  • Thanks to the independent review that found these.

Added

  • Input without chain information in conformance mode (allChains: true): a container address or a processor contract#ID is resolved on BNB Smart Chain, Base and X Layer, each at its own pinned block; two chains resolving it is ambiguous (with candidates), and a chain that cannot be read is reported with its own status instead of a false not-tapeout. It sends reads to the nodes of Base and X Layer, so it is a separate switch and off by default; without it no other chain's public node is ever contacted.
  • Processor number from a container address: the SDK ships a snapshot of each chain's processor table (BSC 1,174, X Layer 263, Base 101), and a hit is used only after one cpuAt at the pinned block returns the same address; otherwise it scans in pages (8 per page, at most 256 per resolution), so a cold client never sends a thousand requests to one node. A container address now gets its on-chain name and the full activation check. node scripts/gen-processors-snapshot.mjs lets anyone recheck the snapshot.
  • The messaging path in conformance mode: tapeSendKey and channelKeys read as TAP-10 says: a fresh pinned block per lookup, strict agreement on every read and on eth_chainId, hub-changed and circuits-changed (TAP-10 §13.8) and the public-key checks (§14.4). The same unactivated container is unpaid to resolve and still has its channel record and TapeSend key read (§12.2). The new optional sealStatusStore keeps the seal status across restarts. tapesend in this mode refuses a chainId above 2^53−1.
  • Strict agreement for the reads that authorise a signer (TAP-11 §2.2): the holder's ownerOf and, for a contract-wallet holder, eth_getCode and isValidSignature (delegation and contentSig) need enough operators to answer, all equal; the eth_chainId check is strict too. The cost, plainly: with the default nodes BNB Smart Chain has 3 operators, so one node down or behind the pinned block makes a conformance-mode resolve unavailable; Base has 4 (3 needed), X Layer 2 (no change). siteStatus and the default mode are unaffected.
  • With allChains, all 23 TAP-10 Test Cases that tap10-gap judges offline conform (18 in 1.4).

Changed

  • siteStatus (any mode): a container address or processor contract#ID now gets its name and processor number, and a cold client reads cpuCount and cpuAt to find them. resolve and the chain.* reads of the default mode are unchanged; a differential test checks every request.
  • In conformance mode without allChains, a processor contract#ID string is unsupported (TAP-10 §4.1 resolves it only when exactly one chain does); { circuits, tokenId } is unaffected. The mode now accepts only the hub, factory and opener TAP-10 lists.
  • TAPI-26 erratum: an invite sent through TapeSend is sealed to the recipient's usable TAP-10 key on the hub, never to the channel key; no implementation had sent or received one.

What you will notice

  • Our own 11.1013.tape and 12.1013.tape were activated by their holder on 2026-10-01 (paid until 2036-08-09); both resolve in conformance mode.

Install

npm install https://github.com/BruceLanLan/tapeapi/releases/download/v1.5.0/tapeapi-sdk-1.5.0.tgz

To install the server alone, install the SDK tgz of the same version first. Full changes in the CHANGELOG. This project has not been audited by a third party.