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-langchain

v0.2.0

Published

Anchor your LangChain.js agent's run tree as it executes: one callback handler turns every chain/LLM/tool step 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-langchain

Anchor your agent's run tree as it executes. Add one callback handler, and every chain, chat-model, LLM, tool, and retriever step becomes a tamper-evident provenance record:

  1. events are Merkle-batched on the hot path — a whole agent session is ONE write to Arweave (via Turbo, ar.io's upload service), not one per step,
  2. every event still gets its own standalone inclusion proof, verifiable offline against the batch checkpoint,
  3. prompts, outputs, and tool I/O are hashed locally and never uploaded — the on-chain envelope carries only the hash (ario.events/v1, Minimal disclosure).
npm install @ar.io/anchor-langchain @ar.io/anchor @langchain/core
import { createAnchorer } from "@ar.io/anchor";
import { anchorCallbacks } from "@ar.io/anchor-langchain";

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

// One line of integration: pass it like any LangChain callback.
const answer = await agent.invoke(
  { input: "Summarize the Q2 incident reports" },
  { callbacks: [provenance] },
);

// End of run / request / 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.

The run tree is the record

LangChain's callbacks carry runId/parentRunId for every step. The handler commits that linkage — plus its own per-run event chain — inside every hash-committed record's metadata:

"langchain": {
  "run_id": "…",          // this step
  "parent_run_id": "…",   // its parent in the run tree
  "root_run_id": "…",     // the top-level invocation it belongs to
  "seq": 3,               // its ordinal within that root run
  "prev_event_id": "…"    // the previous event's envelope event_id
}

Each record points at its predecessor, and every pointer lives inside individually signed, inclusion-proofed bytes. Drop an event and the next one's pointer dangles; reorder them and seq disagrees; edit one and its hash breaks. The result: a deletion-evident, reorder-evident tree of everything the agent did, reconstructable offline from the receipts alone.

Event vocabulary

langchain.chain_start/_end/_error, langchain.chat_model_start, langchain.llm_start/_end/_error, langchain.tool_start/_end/_error, langchain.retriever_start/_end — one event type per anchored callback (exported as EVENT_TYPES).

Controlling what the hash commits to

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

const provenance = anchorCallbacks(anchorer, {
  batch: { maxEvents: 64, flushOnIdle: 2_000 },          // batching knobs (first trigger wins)
  mapPayload: (e) => {
    if (e.type === "langchain.retriever_end") return null; // skip entirely
    return { ...e.payload, prompts: 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 run — the raw step 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 callback is one in-memory batch.add(); signing and the single upload happen at window flush. An agent step never waits on the network.
  • Lifecycle is explicit. await provenance.close() flushes and resolves all receipts; there are no hidden process-exit hooks. Long-lived handlers can flush() between requests.
  • Provenance never crashes the agent. A payload that fails to serialize is reported via warn and skipped — the run continues.
  • Retention is yours. External commitment means the receipt's recordBytes are the only copy of what the hash commits to. Store them (DB row, object store, log archive) — 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.