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

vaid-pop

v0.3.0

Published

The VAID proof-of-possession signing primitive (TypeScript): RFC 8785 (JCS) -> SHA-256 -> Ed25519, byte-identical to the Rust and Python implementations against the frozen conformance vectors.

Readme

vaid-pop (TypeScript)

Canonical TypeScript proof-of-possession (PoP) request signing primitive for the VAID standard.

This is the single TypeScript definition of the PoP signing contract: RFC 8785 (JCS) canonicalization, the per-request payload, the completion record, and the Ed25519 sign/verify over the canonical digest. vaid-mint and vaid-client both depend on it and never reimplement any of it.

import { canonicalRequestSigningBytes, signPayload, verifySignedPayload } from 'vaid-pop';

const payload = {
  vaidId: '11111111-1111-1111-1111-111111111111',
  method: 'POST',
  path: '/vaid/mint',
  bodySha256: 'e3b0c442…',
  tenantId: 'acme',
  timestamp: '2026-06-04T12:00:00Z',
  clientNonce: '0123456789abcdef0123456789abcdef',
};

const signature = signPayload(payload, agentPrivateSeed);   // raw 64 bytes
verifySignedPayload(payload, agentPublicKey, signature);    // true

If you want the HTTP transport (the four x-synthera-* headers), use vaid-client, which builds on this.

Install

npm install vaid-pop

(From a repo checkout: cd typescript && npm install && npm run build --workspaces.)

ESM only, and typed. It runs on Node ≥ 20.19, and — because the crypto comes from @noble/* rather than node:crypto — in browsers, Deno, Bun, and edge runtimes too. CommonJS consumers on Node ≥ 20.19 can require('vaid-pop') via require(esm).

The firewall

Cross-language byte-identity is the whole point. The primitive is locked against the frozen cross-language vectors (vendored here at vectors/), which the Rust crates and the Python packages assert against too. CI proves Rust output == Python output == TypeScript output == vector, byte-for-byte. A mismatch is a hard blocker, not a bug report.

Run the packaged firewall against your installed copy:

npx vaid-pop-conformance      # exit 0 = PASS, 1 = BLOCKER

Contract: RFC 8785 (JCS) over the camelCase payload → SHA-256 → 32-byte digest → pure Ed25519 over that digest as the raw message → raw 64-byte signature.

Two encoding facts that carry the contract

  • Payloads are camelCase. RequestAuthPayload and CompletionRecord use camelCase field names, matching the Rust structs' rename_all = "camelCase". JCS sorts keys, so declaration order is irrelevant — the names are not. (The VAID document in vaid-mint is the exception: it is snake_case.)
  • Timestamps are whole-second RFC 3339 …Z. This is the chrono-serde fixed point: a whole-second …Z string parses and re-serializes to itself, so a client's signed timestamp matches the server's recomputation. Use utcWholeSecondRfc3339()Date.prototype.toISOString() emits milliseconds and would put a sub-second component inside the signed bytes.

What is in here

| Export | What it is | |---|---| | canonicalize / canonicalizeToString | RFC 8785 (JCS) serialization. Rejects values it cannot represent rather than coercing them — silently substituting a value inside signed bytes is the failure mode being prevented. | | canonicalRequestSigningBytes | JCS → SHA-256. The 32-byte digest both sides derive. | | signPayload / verifySignedPayload | Detached Ed25519 over that digest. Verification is a result (false), never a fault. | | RequestAuthPayload | The seven fields a holder signs per request. | | CompletionRecord / AssuranceTier | Signed statement that a VAID-authorized action finished. | | sha256 / toHex / toBase64 / … | The byte helpers the wire format needs. |

Scope of the completion record

CompletionRecord carries exactly one detached signature, by signerVaidId. That proves "this signer signed this record" and nothing more, so assuranceTier is declared, not proven:

  • selfReported is the only tier this repo substantiates on its own.
  • counterSigned and thirdPartyAttested are not independently verifiable here. A self-reporting signer can set either and the single signature still verifies. Treat them as unverified claims.

License

Apache-2.0. See LICENSE.