@antseed/web-sdk

中文文档
0.1.32发布于 2026/11/05TypeScriptES Module近 30 天下载 4463

Browser buyer SDK for AntSeed: connect to sellers over WebRTC via a signaling relay, pay with in-browser EIP-712 signatures

原始使用文档
文档目录8 节
  1. Example page
  2. Durable channel state and multiple tabs
  3. ICE / TURN configuration
  4. Architecture
  5. Persistence and multiple tabs
  6. Known limitation: large non-streamed responses
  7. Prerequisites for paying sellers
  8. Testing

@antseed/web-sdk

Browser buyer SDK for AntSeed. Connects to unmodified sellers over WebRTC DataChannels (signaled through an @antseed/relay) and pays per request with in-browser EIP-712 signatures. No native modules; the only runtime dependency is ethers.

import { AntseedWebClient } from '@antseed/web-sdk';

const client = await AntseedWebClient.create({
  relayUrl: 'https://relay.example.com',
  privateKey: buyerHotWalletKey,        // peerId = its EVM address
  connection: {
    iceServers: [
      { urls: 'stun:stun.example.com' },
      { urls: 'turns:turn.example.com', username, credential },
    ],
    onConnectionInfo: ({ path }) => showPath(path), // direct | relay | unknown
  },
});

const sellers = await client.sellers();
const session = await client.connect(sellers[0].peerId);

const response = await session.request(
  {
    path: '/v1/chat/completions',
    provider: 'openai',
    headers: { accept: 'text/event-stream' },
    body: JSON.stringify({ model: 'gpt-oss-120b', stream: true, messages: [...] }),
  },
  { onChunk: (data, done) => render(new TextDecoder().decode(data)) },
);

Example page

A working single-page example (seller discovery, connect, streaming chat) lives in examples/example.html:

pnpm --filter @antseed/web-sdk run example
# open http://127.0.0.1:8974/example.html

It expects a reachable relay (node apps/relay/dist/index.js, default port 8917 — use RELAY_STATIC_SELLERS=<peerId>@<host>:<port> to pin a local seller) and works unfunded against sellers that accept free requests.

Durable channel state and multiple tabs

By default the client uses an in-memory channel store: on reload the latest signed SpendingAuth state is gone and reserved funds stay locked until the session deadline passes. Production apps should inject a durable store:

const client = new AntseedWebClient({ relayUrl, wallet, channelStore: myStore });

channelStore takes any BuyerChannelStore (re-exported here). For crash-safety implement the optional flush(): it is awaited after each newly signed authorization is persisted and before it is transmitted, so it must not resolve until the write is durably committed (e.g. IndexedDB behind a synchronous in-memory cache). If several tabs can share the store, the implementation must also serialize signing (e.g. navigator.locks) — SpendingAuth amounts are cumulative per channel, and two tabs signing concurrently corrupt the sequence. Without a shared store, each tab opens its own channel and reserves its own budget.

ICE / TURN configuration

connection.iceServers accepts bare URL strings or full RTCIceServer entries (for TURN credentials); connection.iceTransportPolicy: 'relay' forces TURN-only paths. connection.onConnectionInfo reports the selected path ('direct' | 'relay' | 'unknown') once connected, without exposing addresses or raw candidates.

Architecture

This package contains only the browser-specific pieces: relay signaling (hello envelope, SDP offer, trickle ICE), the RTCPeerConnection transport, keepalive, channel-store adapters, and cross-tab coordination. Everything else is shared code, not a reimplementation. Production clients use an IndexedDB store; the in-memory store remains available only through the explicit ephemeral() API:

  • @antseed/protocol — wire format: message types, frame/HTTP/payment codecs, EIP-712 payment signatures, connection auth. Same modules @antseed/node runs.
  • @antseed/buyer-core — the buyer machinery: BuyerRequestHandler, BuyerPaymentManager, BuyerPaymentNegotiator, ProxyMux/PaymentMux. The exact classes behind the node/CLI buyer proxy, wired here to a WebRTC DataChannel and the configured channel store (in-memory by default).

So 402 negotiation, ReserveAuth/AuthAck, NeedAuth cost validation, cumulative SpendingAuth metadata, streaming, and chunked uploads behave identically to the node buyer. One browser-specific override: uploads chunk above 192 KiB (not 512 KiB) to stay under the ~256 KiB SCTP message ceiling browsers negotiate with libdatachannel sellers. Defaults target Base mainnet; override payment.* for other chains.

The relay bridge is closed as soon as the DataChannel opens — it is only needed for signaling, and holding it would consume the relay's per-IP bridge slots. There is no re-signaling path; a dropped DataChannel means a fresh connect().

Persistence and multiple tabs

AntseedWebClient.create() is asynchronous because it acquires an exclusive Web Lock for the buyer identity and hydrates IndexedDB before the payment manager may sign. Only one tab can use a buyer on the same chain/channels contract at a time. A second tab fails with BuyerAlreadyActiveError by default; set persistence.waitForLock to wait instead. A crashed tab releases its Web Lock automatically, and its successor recovers the last durably committed channel state.

Every ReserveAuth and SpendingAuth is committed to IndexedDB before it is sent. The database contains channel identifiers, signatures, cumulative amounts, metadata, and recovery context, but never the buyer private key. Use onPersistenceError to surface background failures from ordinary status/accounting writes immediately; awaited authorization commits and close() also reject when persistence is unhealthy. Call await client.close() when the application is done to flush storage and release the lock. Page-unload handlers are not a channel recovery mechanism.

AntseedWebClient.ephemeral() retains in-memory behavior for tests and free interoperability experiments. It must not be used for paid traffic because a reload loses its channel state and it provides no multi-tab exclusion.

SellerSession.close() closes only the WebRTC transport. Use await session.closeChannel() to request cooperative settlement from a supporting seller. client.listActiveChannels() exposes locally unresolved channels for recovery UI. If the seller is unavailable, call session.requestOnChainClose(operatorSigner) and, after the contract grace period, session.withdrawTimedOutChannel(operatorSigner). The signer must be the operator configured for the buyer in AntseedDeposits; the SDK does not assume that the browser wallet or any particular backend owns that role.

The relay verifies DHT metadata before caching it, and the SDK rejects a non-empty seller snapshot older than maxSellerSnapshotAgeMs (15 minutes by default) before normalizing it into the shared buyer pricing/capability view. The relay remains a trusted discovery-availability boundary; connection auth, seller response authentication, and payment signatures remain end to end.

Known limitation: large non-streamed responses

Chunking currently protects the upload direction only. A seller sends a non-streamed response body as a single DataChannel message, and browsers cap received SCTP messages at ~256 KiB — a larger response (e.g. base64 image output) will kill the DataChannel. Until seller-side response chunking exists, prefer streaming (accept: text/event-stream) for potentially large responses.

Prerequisites for paying sellers

The buyer address must have USDC deposited in AntseedDeposits (directly or via the gasless EIP-3009 sweep). Deposit UX is out of scope for this SDK.

Testing

vitest run covers byte-for-byte parity with @antseed/node (hello envelope, frame codec, EIP-712 signatures, metadata hashing) and an end-to-end suite that drives the real @antseed/node seller listener through a real relay instance, with node-datachannel/polyfill standing in for the browser's RTCPeerConnection. pnpm run test:browser additionally launches real Chromium and Firefox runtimes, connects their native WebRTC stacks to an unchanged seller through the relay, and verifies multi-tab exclusion plus crash takeover. The browser suite also completes paid 402 negotiation, verifies the EIP-712 authorizations, reloads IndexedDB, simulates lost seller state, and recovers the same on-chain channel without issuing a second reserve.

版本与发布记录

@antseed/web-sdk 共发布 33 个版本,首次发布于 2026/08/19;过去 12 个月发布了 33 个版本。

版本发布日期距今
0.1.32latest2026/11/051 天前
0.1.312026/11/041 天前
0.1.302026/11/042 天前
0.1.292026/11/042 天前
0.1.282026/11/033 天前
0.1.272026/11/015 天前

关于 @antseed/web-sdk 的常见问题

@antseed/web-sdk 是什么?

Browser buyer SDK for AntSeed: connect to sellers over WebRTC via a signaling relay, pay with in-browser EIP-712 signatures

如何安装 @antseed/web-sdk?

在项目目录运行 npm install @antseed/web-sdk,或使用 pnpm add / yarn add / bun add。

@antseed/web-sdk 支持 TypeScript 吗?

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

@antseed/web-sdk 支持 ESM 还是 CommonJS?

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

@antseed/web-sdk 可以商用吗?

@antseed/web-sdk 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

@antseed/web-sdk 的下载量有多少?

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

@antseed/web-sdk 还在维护吗?

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