@agentcontextdistributionprotocol/acdp-wasm

中文文档
0.21.4发布于 2026/11/07MIT OR Apache-2.0TypeScriptES Module零依赖构建来源已验证近 30 天下载 2806

Agent Context Distribution Protocol — WebAssembly verification core

原始使用文档
文档目录9 节
  1. Design (same rules as the Python / Node bindings)
  2. Exported surface
  3. Install
  4. Verifying a retrieved body
  5. Verifying a registry receipt
  6. Build
  7. Golden-vector parity
  8. Randomness — a correction to the research memo
  9. Security notes

acdp-wasm — ACDP WebAssembly verification core

The browser / edge member of the ACDP binding family (bindings/acdp-py, bindings/acdp-node). A pure, offline cryptographic verifier: it lets a consumer render an ACDP context and independently reach a real verification verdict — the producer signature, the content_hash, a registry receipt (RFC-ACDP-0010), a lineage-head receipt (RFC-ACDP-0011), a transparency-log checkpoint / inclusion / consistency proof (RFC-ACDP-0012), a lifecycle event (RFC-ACDP-0013), a key revocation (RFC-ACDP-0014), and witness cosignatures + quorum (RFC-ACDP-0015) — without trusting any server to have done it. This is the client-side verification core the console's verdicts consume.

It is a standalone Cargo package (its own [workspace]) that depends on the umbrella acdp crate with default-features = false, so reqwest / tokio / rustls never enter the .wasm binary. See docs/research/wasm-target.md for the (historical) design rationale. The package is released at the same version as the acdp crate; which protocol lines each release implements is recorded in the spec's version matrix. Only wasm32-unknown-unknown (browser / bundler / Node via wasm-pack) is built and tested; WASI targets are untested.

Design (same rules as the Python / Node bindings)

  • JSON across the boundary. Every export takes JSON strings and returns a JSON string — a verdict object ({"valid": true, ...} / {"valid": false, "code"?, "error"}) for verification outcomes, or a result string for constructors/resolvers. Malformed host input throws a JsError; a failed verification is {"valid": false}, never a throw.
  • Crypto in Rust, HTTP in the host. No network calls. did:web resolution and all transport stay in JS (fetch the DID document / receipt / body, pass the JSON in). did:key verification is fully offline and needs no host help — the highest-value browser path.
  • No crypto reimplemented. Every check delegates to the same acdp core the native library, Python, and Node bindings use. The 0.3/0.4 verdict logic (src/v030.rs, src/v040.rs) is lifted verbatim, byte-identical from the other bindings.

Exported surface

wasm-bindgen exports (camelCase in JS/TypeScript):

ExportRFCPurpose
verifyContentHash0001 §5.7recompute sha256(JCS(producer_content))
verifySignatureEd25519 / verifySignatureP2560001 §5.8signature over the ASCII "sha256:<hex>" string
verifyCtxIdBinding0006 §4.1 step 7bind the served ctx_id to the one requested — see Verifying a retrieved body
verifyBodyOffline / verifyPublishRequestOffline0002full did:key context verify (no resolution)
fingerprintEd25519 / verifyReceipt(receiptJson, bodyJson, registryPublicKeyB64, expectedCtxId, recomputedBodyHash, producerKeyFingerprint)0010registry-receipt verification — see Verifying a registry receipt below
verifyLineageHeadReceipt0011lineage-head receipt
verifyLogCheckpoint / verifyLogInclusion / verifyLogConsistency / buildLogLeaf / merkleLeafHash / merkleNodeHash / merkleRootHash0012transparency log
verifyLifecycleEvent0013lifecycle event
parseKeyRevocation / classifyUnderRevocation0014key revocation
buildWitnessCosignature / verifyWitnessCosignature / evaluateWitnessQuorum0015witness cosignatures + quorum
resolveDidKey—offline did:key → public key
canonicalPreimage / explainHashMismatch—hash-divergence diagnostics

Install

Published to npm as @agentcontextdistributionprotocol/acdp-wasm (a public scoped package, provenance-signed on GitHub Actions), so the console and other consumers depend on it without a sibling checkout:

npm install @agentcontextdistributionprotocol/acdp-wasm
import init, { verifyContentHash, verifySignatureEd25519, verifyCtxIdBinding }
  from "@agentcontextdistributionprotocol/acdp-wasm";
await init();
const verdict = JSON.parse(verifyContentHash(bodyJson, body.content_hash));
if (verdict.valid) { /* the hash the consumer recomputed itself checks out */ }

// Bind the served ctx_id to the one requested (RFC-ACDP-0006 §4.1 step 7).
// See "Verifying a retrieved body" below for why this is a separate call.
const binding = JSON.parse(verifyCtxIdBinding(bodyJson, requestedCtxId));
if (binding.valid) { /* the ctx_id you got back is the one you asked for */ }

Verifying a retrieved body

ctx_id is registry-assigned and sits in the RFC-ACDP-0001 §5.7 exclusion set, so it is stripped before content_hash is computed — neither the hash recompute above nor the producer's signature covers it. Without an explicit comparison, a registry could serve any other validly-signed body from the same producer under the ctx_id URL you asked for, and every check above would still pass. verifyCtxIdBinding(bodyJson, expectedCtxId) closes that gap: bodyJson carries the served identity, expectedCtxId is what you requested. Like verifyContentHash, a malformed served ctx_id is a {"valid": false, ...} verdict, not a throw — only a malformed expectedCtxId argument throws a JsError.

Verifying a registry receipt

verifyReceipt (RFC-ACDP-0010 §8) takes the accompanying bodyJson as a required second argument — it binds the receipt's lineage_id, origin_registry, and created_at to the served body's own fields (§8 step 3):

const verdict = JSON.parse(verifyReceipt(
  receiptJson, bodyJson, registryPublicKeyB64,
  expectedCtxId, recomputedBodyHash, producerKeyFingerprint,
));

A malformed bodyJson throws a JsError (host input); a body/receipt mismatch is a {"valid": false, ...} verdict (verification outcome), not a throw — same convention as verifyContentHash / verifyCtxIdBinding. Two checks still stay the HOST's obligation, because neither needs the body: the serving-authority binding (receipt.registry_did must equal the authority you actually fetched from) and recomputing (never trusting) the body hash you pass in.

The package is built with wasm-pack --target web: an ESM module you initialize once with await init(). This is the target the crate is designed and CI-tested around, and it loads cleanly in browsers and in Next.js client components (webpack 5 resolves the new URL(..., import.meta.url) wasm asset the web target emits — no experiments.asyncWebAssembly webpack override, which the bundler target would require).

Build

rustup target add wasm32-unknown-unknown

# Raw wasm (regression build-check):
cargo build --target wasm32-unknown-unknown            # verify-only default

# Browser package (.js / .wasm / .d.ts) with wasm-pack. `--scope` makes
# wasm-pack emit the package name `@agentcontextdistributionprotocol/acdp-wasm`
# (the same rename the release workflow performs):
wasm-pack build --target web --out-dir pkg --scope agentcontextdistributionprotocol

Import from a browser (local, unpublished build):

import init, { verifyContentHash, verifySignatureEd25519, verifyCtxIdBinding } from "./pkg/acdp_wasm.js";
await init();
const verdict = JSON.parse(verifyContentHash(bodyJson, body.content_hash));
if (verdict.valid) { /* the hash the consumer recomputed itself checks out */ }
const binding = JSON.parse(verifyCtxIdBinding(bodyJson, requestedCtxId));
if (binding.valid) { /* the ctx_id you got back is the one you asked for */ }

Golden-vector parity

tests/golden.rs is a native cargo test that runs the canonical sig-001 (content hash + Ed25519 signature) and wit-001 (witness cosignature mint + verify) spec fixtures through the same pure core functions the wasm exports wrap, asserting byte-for-byte reproduction of the pinned golden values — the same constants the acdp-py / acdp-node suites pin. It locates fixtures via ACDP_SPEC_DIR and skips gracefully when absent:

ACDP_SPEC_DIR=../../../agentcontextdistributionprotocol cargo test

tests/wasm.rs is a wasm-bindgen-test real-engine smoke test of the exported wrappers (inline sig-001 constants, no fixtures needed):

wasm-pack test --node

Randomness — a correction to the research memo

docs/research/wasm-target.md §4 predicted a verify-only build would need no getrandom backend on any wasm target. That holds at runtime — no exported verification (nor the deterministic Ed25519 witness mint) draws randomness. It does not hold at compile time for wasm32-unknown-unknown: the core crates pull getrandom 0.4 unconditionally, from two places, and it emits a hard compile_error! on that target unless a backend is wired:

  • acdp-crypto key generation (getrandom::SysRng, used with rand_core 0.10, which has no OsRng); and
  • uuid v4 (a non-optional dependency of acdp-primitives).

The backend is the getrandom wasm_js feature (on the getrandom_wasm alias in Cargo.toml), plus --cfg getrandom_backend="wasm_js" in .cargo/config.toml, which is inert at 0.4.3 and kept for forward compatibility. uuid additionally needs its own js feature for its v4 RNG shim. (An older direct getrandom 0.2 dependency with the js feature, for the former rand_core 0.6 / OsRng path, has been removed; nothing in this binding's graph uses 0.2 any more.)

Both are wired here, target-gated to wasm32 only (see Cargo.toml and .cargo/config.toml), so a native cargo test is untouched. crypto.getRandomValues (the wasm_js backend) is the CSPRNG RFC-ACDP-0001 §5.10 names for the browser, so the choice is spec-blessed — but it is never invoked on the verify path; it is present only to link.

A future producer feature (reserved, not yet wired) would expose fresh key generation (SigningKey::generate, the only SysRng caller) and is where a runtime randomness draw would actually occur.

Security notes

  • This artifact is a verifier, not a fetcher. RegistryClient, CrossRegistryResolver, and did:web resolution do NOT move to wasm; the host owns HTTP and the RFC-ACDP-0006 §7 / RFC-ACDP-0008 SSRF defenses. Pass fetched documents in as JSON.
  • A browser tab is not an HSM. Verification holds no secrets, but a future producer/keygen surface would place the signing seed in wasm linear memory during a call (RFC-ACDP-0001 §5.10).

版本与发布记录

@agentcontextdistributionprotocol/acdp-wasm 共发布 31 个版本,首次发布于 2026/07/07;过去 12 个月发布了 31 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.21.4latest2026/11/07今天
0.21.32026/11/07今天
0.21.22026/11/07今天
0.21.12026/11/071 天前
0.21.02026/11/016 天前
0.19.02026/10/318 天前

关于 @agentcontextdistributionprotocol/acdp-wasm 的常见问题

@agentcontextdistributionprotocol/acdp-wasm 是什么?

Agent Context Distribution Protocol — WebAssembly verification core

如何安装 @agentcontextdistributionprotocol/acdp-wasm?

在项目目录运行 npm install @agentcontextdistributionprotocol/acdp-wasm,或使用 pnpm add / yarn add / bun add。

@agentcontextdistributionprotocol/acdp-wasm 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@agentcontextdistributionprotocol/acdp-wasm 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@agentcontextdistributionprotocol/acdp-wasm 可以商用吗?

@agentcontextdistributionprotocol/acdp-wasm 使用 MIT OR Apache-2.0 许可。可在 MIT、Apache-2.0 中任选其一遵守。选择 MIT 时,宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@agentcontextdistributionprotocol/acdp-wasm 的下载量有多少?

根据 npm 官方下载统计,@agentcontextdistributionprotocol/acdp-wasm 在 2026/10/05 – 2026/11/03 这 30 天内被下载 2,806 次,日均约 94 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@agentcontextdistributionprotocol/acdp-wasm 还在维护吗?

最新版本 0.21.4 发布于 2026/11/07(今天);至今共发布 31 个版本,其中过去 12 个月发布了 31 个;首次发布于 2026/07/07。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。