
Research
/Security News
PolinRider Spreads Through Compromised GitHub Accounts and Packagist
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.
@payperbyte/sdk
Advanced tools
PayPerByte TypeScript SDK — cryptographically attested, provenance-verifiable data for AI agents: proves which publisher signed the exact bytes (authenticity + tamper-evidence, not data correctness). Pay-per-call via x402 in USDC on Base mainnet; every pa
TypeScript SDK for PayPerByte (the BYTE Library) — the cryptographically attested, provenance-verifiable data layer for AI agents. Discover first-party feeds, subscribe, stream payloads, and verify every payload against its on-chain EIP-712 attestation (provenance + tamper-evidence — who signed these exact bytes — not a correctness guarantee). No token; x402 USDC payments settle on Base mainnet (the on-chain subscribe + EIP-712 attestation rail is Arbitrum).
npm install @payperbyte/sdk
import {
ByteClient,
Subscriber,
Mercat,
verifyFromEvent,
ARBITRUM_SEPOLIA,
} from "@payperbyte/sdk";
// `network` is a NetworkConfig object (ARBITRUM_SEPOLIA / LOCAL_ANVIL),
// not a string. RPC URL + contract addresses come from that config.
const client = new ByteClient({ network: ARBITRUM_SEPOLIA });
// Discover publishers and their feeds via the indexer (Mercat).
const mercat = new Mercat(ARBITRUM_SEPOLIA.indexerUrl);
const publishers = await mercat.search({ topic: "eth-price" });
// Subscribe to a data feed — r2 DIRECT-ALLOWANCE model. There is NO escrow.
// subscribe(publisher, allowanceUsdc) does two on-chain things:
// 1. dataRegistry.subscribe(publisher) — the social-registry flag, and
// 2. usdc.approve(dataStream, cap) — the spending cap the publisher's
// streamData/streamBroadcast transferFrom-pulls each per-message fee from.
const subscriber = new Subscriber({
network: ARBITRUM_SEPOLIA,
privateKey: "0x...",
});
await subscriber.subscribe(publishers[0].address, 10.0); // $10 allowance cap
// Verify-before-act: recompute the hash AND recover the signer before trusting a
// single byte. verifyFromEvent is the FULL check for an on-chain attestation; the
// x402 gateway path uses verifyFromGatewayResponse (see Foreseal Kit below). The
// hash-only verifyPayload() is the lower-level leg — prefer the full check.
const verdict = await verifyFromEvent(message, receivedBytes, ARBITRUM_SEPOLIA);
if (!verdict.verified) throw new Error(verdict.reason); // do not act on unverified bytes
For one-off, pay-per-call access there is the keyless x402 GatewayClient. A
wallet signs the payment (gasless EIP-3009 transferWithAuthorization — the
facilitator broadcasts and pays gas). There is no API key: the wallet is the
credential. The @x402/core + @x402/evm packages are optional peer deps,
loaded only if you use the gateway.
import { privateKeyToAccount } from "viem/accounts";
import { createPublicClient, http } from "viem";
import { base } from "viem/chains";
import { GatewayClient } from "@payperbyte/sdk";
const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`);
// x402 payments settle on Base mainnet (eip155:8453). publicClient is OPTIONAL for
// the gasless "exact" path — the facilitator broadcasts; pass one only for on-chain reads.
const publicClient = createPublicClient({ chain: base, transport: http() });
// Defaults to https://x402.payperbyte.io; pass baseUrl for local dev (:3402).
const gw = new GatewayClient({ signer: account, publicClient });
// Discover the catalog (free) — each feed carries its priceAtomic.
const { feeds } = await gw.discover();
// Pay-per-call a GET feed: unpaid → 402 → wallet signs USDC → retry → data.
const { data, settlement, disclaimerCategory } = await gw.fetchFeed("weather");
// settlement can be null even on a genuinely paid call — the gateway
// sometimes omits/the client can't decode the receipt header; the 2xx body
// is still real either way (see GatewayFetchResult). Only read the tx hash
// when a receipt actually decoded.
console.log(settlement?.transaction); // on-chain settlement tx hash, or undefined
// POST oracle (address-reputation): a synchronous signed verdict — pass the query
// body; the paid 200 returns { answer, attestation } you verify before acting.
// No method needed: fetchFeed reads the live catalog (GET /feeds) to see this
// feed only answers POST, and uses that — an explicit `method` always wins if
// you pass one, this auto-derivation only fills in when you don't.
await gw.fetchFeed("address-reputation", {
body: { domain: "github.com", address: "0x1111111111111111111111111111111111111111" },
});
Two distinct USDC flows: the on-chain direct-allowance
approve(dataStream)at subscribe time (Subscriber) is independent of the x402 EIP-3009 sign at fetch time (GatewayClient). Pay-per-call via the gateway needs no prior subscription — the wallet signature is the only credential.
The Foreseal Kit is the SDK's headline primitive: sign, verify, and read a publisher's quality score — so any agent can produce and fully verify a PayPerByte PayloadAttestation without us in the request path.
The legacy verifyPayload() checks only the hash (keccak256(bytes) == attestedHash).
The Foreseal Kit adds the missing signer leg: it recovers the EIP-712 attestation
signer and confirms it is the publisher the catalog says it is. A single Verdict
composes both legs.
import {
signAttestation,
verifyFromGatewayResponse,
verifyFromEvent,
verifyAttestation,
getPQS,
ARBITRUM_SEPOLIA,
} from "@payperbyte/sdk";
// 1) verifyFromGatewayResponse — the headline call for the x402 gateway path (what
// most agents use). It is the FULL decision: recompute keccak256(responseBody) AND recover
// the EIP-712 signer under the net-pinned consensus domain, refuse a forked wire
// domain, and assert the signer is the gateway attester you pinned out-of-band
// (REQUIRED — a self-asserted header can't prove provenance; omitting it fails closed).
const body = await res.text(); // the EXACT paid-200 bytes
const header = res.headers.get("X-BYTE-Attestation"); // the raw receipt header
const verdict = await verifyFromGatewayResponse(body, header, ARBITRUM_SEPOLIA, knownGatewayAttester);
// Verdict: { verified, hashMatch, signerMatch, recovered, expired, reason }
if (!verdict.verified) refuse(verdict.reason); // do NOT act on unverified bytes
// On-chain anchor (subscriber/stream path), same EIP-712 domain:
await verifyFromEvent(event, receivedBytes, ARBITRUM_SEPOLIA);
// Lower-level: verifyAttestation takes the fields explicitly (the call the two
// wrappers above compose). verifyPayload() is the hash-only leg — prefer the above.
const v2 = await verifyAttestation({
payloadBytes: receivedBytes,
attestation: event.attestation,
expectedPublisher: publisherAddr,
payloadHash: event.payloadHash,
payloadLength: event.payloadLength,
deadline: event.attestationDeadline,
net: ARBITRUM_SEPOLIA,
});
// 2) sign — produce an attestation (any viem WalletClient/Account).
const sig = await signAttestation(
{ publisher: account.address, payloadHash, payloadLength, deadline },
account,
ARBITRUM_SEPOLIA,
);
// 3) getPQS — read the indexer delivery-quality composite (BPS 0-10000).
const pqs = await getPQS(publisherAddr, ARBITRUM_SEPOLIA.indexerUrl);
// { composite, dispute, retention, freshness, revenueQuality, asOf }
// composite === null → publisher not yet scored.
Verdict rules (these are the contract):
| Case | hashMatch | signerMatch | verified |
|---|---|---|---|
| Known-good | true | true | true |
| Tampered bytes | false | — | false |
| Wrong/forged signer | true | false | false |
| Empty/missing attestation | (computed) | null | false — fail-closed |
"0x") or null
attestation yields signerMatch=null and verified=false. We never "pass on the
hash alone" — provenance is unproven without the publisher's signature.now+300s deadline elapses on
every aged feed. verifyAttestation sets expired=true but does not fail
verified on the immutable on-chain anchor — staleness belongs to a freshness
axis, not the provenance verdict. The caller decides policy.getPQS is off-chain, advisory, and may
be absent.BYTE Library EIP-712 domain literal is consensus-critical and identical
across the on-chain contract, gateway, MCP, and SDK. It is never renamed.approve(dataStream), no escrow)keccak256(bytes) against the on-chain hash before acting on the data| Network | Chain ID | Role | Status |
|---|---|---|---|
| Base | 8453 | x402 USDC payment settlement (GatewayClient) | Live (mainnet) |
| Arbitrum Sepolia | 421614 | On-chain subscribe + EIP-712 attestation anchor | Live (testnet) |
| Arbitrum One | 42161 | Attestation mainnet re-anchor | Planned (audit-gated) |
PayPerByte runs on the BYTE Library — a lean 3-contract core. No token; all settlement is in USDC via a direct-allowance model (the subscriber approves DataStream; the publisher transferFrom-pulls each per-message fee — there is no escrow contract). Each payload carries an EIP-712 PayloadAttestation so subscribers can confirm exactly what they received and from whom.
| Contract | Role |
|---|---|
| DataRegistryLib | Publisher registration, feed/subscriber discovery |
| DataStreamLib | Per-call / per-byte payload delivery + settlement |
| SchemaRegistry | Feed schema + methodology references |
Contract addresses are resolved per-network by the SDK (ARBITRUM_SEPOLIA, LOCAL_ANVIL).
The primary verify path is byte-exact: hash the exact bytes you received against the attested hash. That path needs no canonicalization at all and is the strongest tamper evidence the SDK offers. Prefer it whenever you hold the delivered bytes.
Canonicalization only enters when a payload is re-serialized — and the stack has two canonical-JSON forms, not one:
canonical.ts): recursively key-sorted, no whitespace. Matches the
Python SDK for payloads that keep values to strings/bools/ints; floats, huge ints, and
non-BMP keys are explicitly out of scope (this is NOT full RFC 8785/JCS).data-feeds): INSERTION-ORDER compact JSON — a frozen
hash-compatibility surface that must never be re-sorted.A payload signed under one form will not hash-match a re-derivation under the other, so
fetchAndVerify is form-aware: it tries the raw response bytes and every known form, and
if none reproduces the attested hash it throws CanonicalFormMismatchError — deliberately
NOT HashMismatchError, because a failed re-serialization cannot distinguish tampering from a
form mismatch. Fail closed either way: don't consume the payload; fetch the exact delivered
bytes and use byte-exact verifyPayload.
ByteClient — low-level client holding the viem clients and contract instances (used by Publisher/Subscriber)Publisher — register a feed, publish data, sign EIP-712 PayloadAttestationsSubscriber — subscribe, receive payloads, stream eventsverifyPayload / verifyEventPayload — byte-exact hash-only payload verification against on-chain attestationsfetchAndVerify — archive fetch + form-aware verification; throws CanonicalFormMismatchError (fail-closed) rather than a false tamper alarm when a re-derivation matches no known canonical formsignAttestation, verifyAttestation / verify (hash and signer recovery), verifyFromEvent, verifyFromGatewayResponse, getPQSMercat — feed search and discovery (connects to the indexer API)GatewayClient — keyless x402 pay-per-call client (a wallet signs, not an API key); discover, discoverResources, fetchFeedWant the pre-wired, deploy-ready kit? MCP + Verify-Before-Act Agent Starter Kit — give an agent a wallet that verifies receipts before acting. The SDK here stays free MIT; the kit is the assembly + walkthrough. $39 → https://payperbyte.gumroad.com/l/pvykda
MIT
FAQs
PayPerByte TypeScript SDK — cryptographically attested, provenance-verifiable data for AI agents: proves which publisher signed the exact bytes (authenticity + tamper-evidence, not data correctness). Pay-per-call via x402 in USDC on Base mainnet; every pa
The npm package @payperbyte/sdk receives a total of 19 weekly downloads. As such, @payperbyte/sdk popularity was classified as not popular.
We found that @payperbyte/sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.

Company News
Allow myself to introduce... myself.