npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ar.io/anchor-vercel

v0.2.0

Published

Anchor your Vercel AI SDK calls as they run: one language-model middleware turns every generate/stream into a Merkle-batched, individually inclusion-proofed provenance record on ar.io — prompts and outputs are hashed locally and never uploaded.

Readme

@ar.io/anchor-vercel

Anchor your AI SDK calls as they run. Add one language-model middleware, and every generateText / streamText becomes a tamper-evident provenance record:

  1. calls are Merkle-batched on the hot path — a whole request's calls are ONE write to Arweave (via Turbo, ar.io's upload service), not one per call,
  2. every call still gets its own standalone inclusion proof, verifiable offline against the batch checkpoint,
  3. prompts, settings, and outputs are hashed locally and never uploaded — the on-chain envelope carries only the hash (ario.events/v1, Minimal disclosure).
npm install @ar.io/anchor-vercel @ar.io/anchor ai
import { createAnchorer } from "@ar.io/anchor";
import { anchorMiddleware } from "@ar.io/anchor-vercel";
import { generateText, wrapLanguageModel } from "ai";
import { openai } from "@ai-sdk/openai"; // your model provider — install whichever you use

const provenance = anchorMiddleware(createAnchorer()); // dev mode: zero config

const model = wrapLanguageModel({ model: openai("gpt-4o"), middleware: provenance });

await generateText({ model, prompt: "Summarize the Q2 incident reports" });

// End of request / run / process: flush and collect the proofs.
const receipts = await provenance.close();
for (const r of receipts) {
  console.log(r.envelope.event_id, "→ checkpoint", r.checkpointTxId, `leaf ${r.leafIndex}/${r.leafCount}`);
  // r.recordBytes is YOUR copy of what the hash commits to — retain it.
}

Production refuses auto-generated secrets — createAnchorer({ environment: "production", signer, wallet }) per @ar.io/anchor's structural gate. Dev proofs are permanently marked environment: "dev" inside the signed bytes.

Grouping calls into a chain

The Vercel AI SDK middleware wraps the model, not the application, so — unlike @ar.io/anchor-langchain, whose callbacks give a run tree — events here form a flat chain (seq + prev_event_id, committed in each record's metadata). You choose what groups them:

  • Correlation id (recommended for multi-call requests): pass an id via providerOptions.ario.chainKey, and every call sharing it links in order — your request id, conversation id, or agent-loop id.

    await generateText({
      model,
      prompt,
      providerOptions: { ario: { chainKey: requestId } },
    });
  • Session fallback (zero-config): with no id, all calls through one middleware instance link in emission order under a per-instance session:<uuid> chain.

Either way it's a flat sequence, not a tree: each record points at its predecessor inside individually signed, inclusion-proofed bytes, so a dropped event dangles, a reordered one disagrees on seq, and an edited one breaks its hash — deletion-evident and reorder-evident for the calls present.

Event vocabulary

vercel_ai.generate_start/_end/_error and vercel_ai.stream_start/_end/_error — one event type per anchored operation (exported as EVENT_TYPES). Each operation gets exactly one terminal event: _end on success, _error on failure. For streams, a provider failure arrives in-band as an error part and anchors stream_error; chunks pass through untouched. A hard transport abort (the stream rejects with no error part) anchors neither terminal event — the stream_start stands and its missing completion is itself the signal (its chain pointer dangles).

Controlling what the hash commits to

Nothing leaves your process either way — but the committed record is what you retain and what an auditor asks for. mapPayload runs before the hash is computed:

const provenance = anchorMiddleware(anchorer, {
  batch: { maxEvents: 64, flushOnIdle: 2_000 },     // batching knobs (first trigger wins)
  mapPayload: (e) => {
    if (e.type === "vercel_ai.generate_start") return null; // skip entirely
    return { ...e.payload, prompt: undefined };             // or redact fields
  },
});

Skipped events consume no sequence number — the committed chain stays gapless.

Verifying

Collect the receipts, serialize them into ONE signed, portable trace-bundle.json, and hand it to an auditor — they verify the whole thing (every event's signature + payload binding + Merkle inclusion, offline) with one command and the read-only @ar.io/proof (no write SDK in the trust path):

const receipts = await provenance.close();
const bundle = await ario.bundle(receipts); // signed with the anchorer's own key — zero ceremony
await fs.writeFile("trace-bundle.json", JSON.stringify(bundle, null, 2));

Want the auditor to read the actual calls — the raw prompt/response bytes, not just verify their hashes? Pass ario.bundle(receipts, { disclose }) (keyed by eventId) to embed selected events' bytes inside the signed bundle — opt-in, default off, on-chain footprint unchanged. See disclosure in the core README.

npx @ar.io/proof verify trace-bundle.json
# optionally re-fetch each checkpoint on-chain to confirm it's anchored:
npx @ar.io/proof verify trace-bundle.json https://arweave.net,https://permagate.io

The whole story is five steps: anchor → await provenance.close()await ario.bundle(receipts) → write trace-bundle.jsonnpx @ar.io/proof verify trace-bundle.json. Need an explicit key or to override the issuer/gateway? The advanced form is toEvidenceBundle(receipts, { signer, issuer }) from @ar.io/anchor.

It prints a per-event + rollup verdict and exits on a pinned code (0 verified · 1 failed · 2 malformed · 3 gateway-unavailable); the producer's asserted verdict is shown but never trusted (the verdict is recomputed from the body). A withheld record surfaces as semantics-undetermined (~), not a failure.

Live: the bundle emit (ario.bundle() / toEvidenceBundle) ships in @ar.io/anchor ≥ 0.1.3 and the npx @ar.io/proof verify CLI in @ar.io/proof ≥ 0.2.2. The @ar.io/proof primitives below remain available for manual/advanced verification.

A single receipt also verifies by hand against the read-only kernel (@ar.io/proof ^0.2.0, the full-family verifier):

import { verifyEnvelope, verifyInclusion, hexToBytes } from "@ar.io/proof";

// Supply the retained record bytes and the envelope verifies green end-to-end.
const result = await verifyEnvelope(r.envelope, { payloadBytes: r.recordBytes });
result.ok;            // true — signature + payload binding
const inclusionOk = await verifyInclusion(
  hexToBytes(r.leafHash), r.leafIndex, r.leafCount,
  r.auditPath.map(hexToBytes), hexToBytes(r.root),
);

Without the record (external commitment), verifyEnvelope(r.envelope) confirms the signature but reports payloadHashOk: nullsemantics-undetermined, not a failure. Treat null as "supply the record to complete the proof," never as a pass and never as a tamper; a genuinely tampered record returns payloadHashOk: false with ok: false. The checkpoint is fetched through any ar.io gateway (r.gatewayUrl) and re-verified the same way — the gateway is delivery, never trust.

Semantics

  • Hot path is synchronous. Each call is one in-memory batch.add(); signing and the single upload happen at window flush. A model call never waits on the network for anchoring.
  • Errors are observed, never swallowed. A failed call anchors a *_error event and the original error re-throws to your code unchanged.
  • Lifecycle is explicit. await provenance.close() flushes and resolves all receipts; there are no hidden process-exit hooks. Long-lived middleware can flush() between requests.
  • Provenance never crashes the call. A payload that fails to serialize is reported via warn and skipped — the model call still runs and returns.
  • Bounded memory on long-lived servers. One middleware instance batches across requests (keep it long-lived to amortize writes), and it tracks a little chain state per distinct chainKey. That map is a bounded LRU (maxTrackedChains, default 10,000) — a correlation id evicted after long inactivity simply restarts at seq 0 if it ever returns, so per-request ids never accumulate without bound.
  • Retention is yours. External commitment means the receipt's recordBytes are the only copy of what the hash commits to. Store them — an envelope without its record proves a commitment existed, not what it said.
  • Provenance, not endorsement: a verified history says what happened — never "safe" or "approved".

License

MIT. The framework is a peer dependency; this package depends only on @ar.io/anchor.