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

@atrib/verify

v0.2.6

Published

Verifier library for atrib. Independent signature, graph-derivation, and settlement-recommendation checks per atrib spec §4.6.

Readme

@atrib/verify

Independent verification of atrib records and settlement documents. Re-runs the spec §4.6 calculation algorithm locally and checks the result against what a recommendation document claims. Verifies any signed record against its creator key. No trust in any intermediary required.

This is the verifier half of the atrib protocol, used by merchants closing transactions, auditors checking agent activity, regulators querying historical state, and any party that needs to validate atrib data independently. The agent and tool servers produce signed attribution records. The Merkle log stores them. This package answers the questions any verifier has to answer: given the graph and the policy, is this distribution actually correct? Was this record actually signed by the key it claims? Did this action actually happen at the time it claims?

Quick start

import { AtribVerifier } from '@atrib/verify'

const verifier = new AtribVerifier({
  merchantKey: process.env.ATRIB_MERCHANT_KEY, // optional, base64url Ed25519 seed
  graphEndpoint: 'https://graph.atrib.dev/v1', // defaults to atrib.dev endpoints
  logEndpoint: 'https://log.atrib.dev/v1',
})

const result = await verifier.verify(recommendationDoc)
// {
//   valid: true,
//   signatureOk: true,
//   calcMatch: true,
//   distribution: { 'sha256:...': 0.4, 'sha256:...': 0.6 },
//   warnings: [],
//   graph_node_count: 7,
// }

valid === true means both the document's Ed25519 signature verified against the calculator's published key and the local recalculation produced the same distribution within 1e-9. Either failing flips valid to false and the specific failure is reported in signatureOk / calcMatch / warnings so you know exactly what went wrong.

What verify() actually does (per spec §5.5.2)

  1. Resolves the calculator's public key from recommendationDoc.calculated_by. For the well-known resolve.atrib.dev service, the key is fetched from the /pubkey endpoint. For other calculators, the merchant supplies the key out-of-band.
  2. Verifies the Ed25519 signature over the JCS-canonicalized recommendation document (excluding the signature field).
  3. Fetches the attribution graph at recommendationDoc.graph_tree_size from the configured graph endpoint. Pinning to a specific tree size makes the verification reproducible; the graph is fixed, not "live."
  4. Fetches the session policy record referenced by policy_record_id, or uses the spec §4.3 default policy if policy_record_id === 'default'.
  5. Re-runs calculate(graph, policy, sessionPolicyRecord) locally; a pure function with no network calls and no randomness.
  6. Compares distributions using distributionsMatch() (within 1e-9 per recipient, accounting for floating-point drift across implementations).

The key invariant per spec §4.6: any party with the same graph and the same policy MUST get the same distribution. If they don't, either the calculator cheated, the document was tampered with, or one party has a buggy implementation. Either way the merchant should not pay against this document.

Post-hoc calculation (§5.5.3)

If the agent that drove the session was not atrib-aware (no @atrib/agent middleware), the merchant can still produce a signed recommendation after the fact, as long as the tools were attributed:

const recommendation = await verifier.calculate({
  context_id: 'sess_abc123...',
  policy: 'default', // or a full PolicyDocument
  signWith: 'merchant', // signs with merchantKey if present
})
// → fully-shaped RecommendationDocument, ready to settle against

Per the §5.8 degradation contract, this never throws on a missing key; if signWith === 'merchant' but merchantKey is unset, the document is returned unsigned with a warning rather than crashing the merchant pipeline.

API reference

new AtribVerifier(options)

| Field | Type | Default | Description | | ----------------- | -------- | ----------------------------- | ----------------------------------------------------------------------- | | logEndpoint | string | https://log.atrib.dev/v1 | The Merkle log to fetch checkpoints and proofs from. | | graphEndpoint | string | https://graph.atrib.dev/v1 | The graph query endpoint (spec §3). | | resolveEndpoint | string | https://resolve.atrib.dev/v1 | Reserved for v2 remote calculation. | | merchantKey | string | unset | Base64url Ed25519 32-byte seed. Optional. verify() works without it. |

verify(doc): Promise<VerificationResult>

Independently re-runs the §4.6 calculation and verifies the document signature. Always returns a result object; never throws. Inspect valid, signatureOk, calcMatch, and warnings to understand the outcome.

This method operates on RecommendationDocument shapes (settlement-recommendation flow per spec §5.5.2). For verifying individual AtribRecords, see verifyRecord below.

verifyRecord(record, options): Promise<RecordVerificationResult>

Per-record verification. Verifies a single signed record's Ed25519 signature and surfaces per-record annotations defined by spec sections §1.2.5 (D041), §1.2.6 (D044), §6.7 (D051), and §8.4 (D045).

import { verifyRecord } from '@atrib/verify'

const result = await verifyRecord(record, {
  upstreamCandidate,         // optional, for provenance_token resolution
  informedByCandidates: [],  // optional, for informed_by[] resolution
  identityClaim,             // optional, for capability_check (caller does directory lookup)
})
// result: {
//   valid: boolean
//   signatureOk: boolean
//   posture: { timestamp_granularity, timestamp_consistent, timestamp_granularity_explicit }
//   provenance?:              { token, upstream_record_hash, upstream_resolved }
//   informed_by_resolution?:  { resolved: string[], dangling: string[] }
//   capability_check?:        { envelope, in_envelope, mismatches, unresolvable }
//   warnings: string[]
// }

Implemented per-record annotations:

  • provenance: { token, upstream_record_hash, upstream_resolved } per session-genesis record carrying provenance_token (D044 / §1.2.6). The 16-byte token truncation is irreversible: upstream_record_hash populates only when the caller supplies a candidate whose canonical-form SHA-256[:16] matches the token.
  • informed_by_resolution: { resolved: string[], dangling: string[] } per record carrying informed_by (D041 / §1.2.5). Dangling references are flagged but do not fail verification: they signal "the verifier has not seen upstream context," not "the record is invalid."
  • posture: { timestamp_granularity, timestamp_consistent, timestamp_granularity_explicit, args_commitment_form, result_commitment_form, tool_name_form } (D045 / D061 / §8.2 / §8.3 / §8.4). Always populated. Surfaces (a) the declared timing granularity, whether the timestamp value structurally matches the spec's trailing-zero invariant, and whether the field was explicitly set vs defaulted; (b) the structurally-detected args_hash / result_hash commitment scheme: 'salted-sha256' when args_salt / result_salt is present, 'plain-sha256' otherwise (the 'hmac-sha256' variant from §8.3 is signaled out-of-band and is not structurally detectable); and (c) the §8.2 tool_name_form: 'hashed' when tool_name matches ^sha256:[0-9a-f]{64}$, 'plain' for any other present value, null when the field is absent. Per D061 the §8.2 verbatim-vs-opaque distinction is NOT structurally detectable — both surface as 'plain'.
  • capability_check: { envelope, in_envelope, mismatches, unresolvable } (D051 / §6.7). Populated only when the caller passes a resolved identityClaim in options. Checks the record's event_type against the envelope's event_types allowlist and the record's timestamp against expires_at. tool_names (against tool_call records), max_amount, and counterparties (against transaction records) flag unresolvable: true because the constraints depend on data not yet on the standard record shape (tool_name) or out-of-band protocol events (payment amount + counterparty). Per §6.7.3 out-of-envelope is a signal, not invalidation: mismatches do not flip valid to false. The caller is responsible for fetching the active envelope at the record's timestamp via @atrib/directory's lookup() (or a cached equivalent); @atrib/verify intentionally has no @atrib/directory dependency.
  • cross_attestation: { signers_count, signers_valid, missing } (D052 / §1.7.6). Populated only on transaction records (event_type = transaction). Each entry in signers[] is verified against the cross-attestation canonical bytes (JCS form with signers: [] and the top-level signature field omitted, per §1.7.6). missing: true when fewer than 2 signers verify — atrib's normative minimum. Per §1.7.6 missing is a SIGNAL not invalidation: valid stays true if the underlying signature path holds. Legacy single-signer transaction records (no signers[] array, only top-level signature) surface as signers_count: 0, missing: true so consumers can flag them while accepting the cryptographic validity.

Pending per-record annotations (tracked as a Pending decision in DECISIONS.md P005):

  • cross_log_proof_count / cross_log_threshold_met / cross_log_equivocation_detected (D050 / §2.11): requires multi-log proof-bundle parsing and trusted-log-set config. (Note: tool_name_form, args_commitment_form, and result_commitment_form per §8.2/§8.3 are all now implemented under posture above. D061 added tool_name, args_hash, and result_hash to the §1.2.1 canonical record schema, completing the structural inputs.)

Each pending annotation is its own ADR scope when external consumers need it.

calculate(options): Promise<RecommendationDocument>

Post-hoc calculation when no agent SDK was present. Always returns a fully-shaped document, unsigned with a warning if the merchant key is missing.

Lower-level primitives

For advanced use (custom calculators, alternative signing flows), the package also exports:

  • calculate(graph, policy, sessionPolicyRecord): the pure §4.6 calculation function
  • DEFAULT_POLICY: the spec §4.3 default policy document
  • isValidPolicy(doc): schema check for PolicyDocument
  • signRecommendation(unsigned, privateKey): JCS + Ed25519 signing
  • verifyRecommendationSignature(doc, publicKey): signature verification
  • recommendationSigningInput(doc): the canonical bytes that get signed
  • distributionsMatch(a, b): float-tolerant equality (within 1e-9 per recipient)
  • fetchGraph(endpoint, contextId, treeSize?), fetchSessionPolicyRecord, fetchPolicyDocument

Why pure functions matter

The §4.6 calculation algorithm is intentionally a pure function of (graph, policy):

  • No network calls during calculation. The graph and policy are fetched up front and then calculate() runs in-memory.
  • No timestamps beyond those already embedded in the records. Two runs an hour apart on the same inputs produce the same output.
  • No randomness. No "tie-breaker by hash of current time" or anything like that. Ties are broken deterministically per the spec.
  • No floating-point ordering surprises. The algorithm walks the graph in a deterministic order so two implementations on identical input produce identical output (within 1e-9 for the final distribution shares).

This is what makes verification possible: the merchant's local recalculation is the same code the calculator ran, producing the same output, so any disagreement is a real signal; not implementation drift.

§5.8 degradation contract

Per the absolute invariant (also enforced in @atrib/mcp and @atrib/agent), atrib failures never break the host:

  • Missing or invalid merchantKey → constructor logs atrib: ... warning, merchantPrivateKey = null, no throw.
  • verify() errors during signature resolution, graph fetch, or calculation are caught and surfaced as warnings: string[] with valid = false.
  • calculate({ signWith: 'merchant' }) with a missing key returns an unsigned document plus a warning, rather than throwing.

The merchant's payment pipeline never crashes because of an atrib problem. It just gets valid: false and decides what to do with that.

Test coverage

184 tests across 10 test files covering the §4.6 calculation algorithm, graph endpoint client, JCS canonicalization, Ed25519 signing, settlement recommendations, policy templates, policy builder, calculation edge cases, property-based testing with fast-check, and full verify() / calculate() paths including §5.8 degradation.

Run them with pnpm --filter @atrib/verify test.

Spec references

| Spec section | What this package implements | | ------------ | ---------------------------------------------------- | | §3 | Graph query interface (client side) | | §4.3 | Default policy document | | §4.6 | Pure calculation algorithm | | §4.7 | Recommendation document signing/verification | | §5.5 | AtribVerifier class. verify() and calculate() | | §5.8 | Degradation contract; failures never break the host |

The full protocol spec is at atrib-spec.md.

See also

  • @atrib/mcp, server-side middleware that produces the signed records verify() ultimately validates
  • @atrib/agent, agent-side interceptor + framework adapters
  • @atrib/log-dev, development-mode Merkle log stub. Returns placeholder Merkle hashes that will not pass strict cryptographic verification, fine for end-to-end shape testing, not for production verification.
  • packages/integration/examples/end-to-end/, runnable demo wiring everything together
  • DECISIONS.md, architectural decision log

A note on documentation links. The atrib protocol repository is currently private (in-progress public preparation). Links in this README to the spec and sister packages (atrib-spec.md, packages/agent/README.md, etc.) point at github.com/creatornader/atrib/blob/main/... URLs that will resolve once the repository goes public. Until then, see atrib.dev for the protocol overview.