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

v0.3.0

Published

The VAID proof-of-possession request signer (TypeScript): signs requests into the four x-synthera-* PoP headers, byte-identical to the Rust and Python clients.

Readme

vaid-client (TypeScript)

The TypeScript request signer for the VAID standard: assemble (method, path, body) into the canonical proof-of-possession payload, sign it, and emit the four x-synthera-* headers a conforming verifier checks.

import { RequestSigner, popHeaderRecord } from 'vaid-client';

const signer = new RequestSigner(vaidDocumentJson, agentPrivateSeed);
const headers = signer.signHeaders('POST', '/vaid/mint?tenant=acme', body);

await fetch(url, { method: 'POST', body, headers: popHeaderRecord(headers) });

The x-synthera-* header names are the VAID wire contract — the fixed header namespace a conforming verifier reads, not a package dependency.

Install

npm install vaid-client

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

ESM only, and typed. Node ≥ 20.19; CommonJS consumers on that version can require('vaid-client') via require(esm).

Two key custodies

| | Holder holds the key | Key stays in a keystore | |---|---|---| | Class | RequestSigner | PortRequestSigner | | Takes | a raw 32-byte Ed25519 seed | an OperatorSigningPort | | The key | in process | never leaves the keystore |

The port is handed the already-canonical 32-byte digest — never the payload, never the key. Both paths canonicalize through the same vaid-pop primitive, and the conformance suite asserts they produce identical signatures.

import { PortRequestSigner, type OperatorSigningPort } from 'vaid-client';

const port: OperatorSigningPort = {
  async sign(digest) { return kms.signEd25519(keyId, digest); },
  async publicKey() { return kms.publicKey(keyId); },
};

const headers = await new PortRequestSigner(vaidDocumentJson, port)
  .signHeaders('POST', '/vaid/mint', body);

The path convention — a security decision

The signed path is the on-the-wire request target, including the query string, not path-only. Signing path-only would leave the query outside the signature and therefore tamperable: ?limit=10 could be rewritten to ?limit=1000000 under a signature that still verifies.

This is pinned by the frozen pathquery_v1.json vector, and the suite asserts that signing path-only produces a different signature — so the convention cannot quietly regress.

Identity comes from the VAID, never from the caller

vaidId and tenantId are read out of the VAID document you construct the signer with — they are not arguments to signHeaders. A caller can therefore only ever produce a valid signature for its own tenant. Note that the document is snake_case (vaid_id, tenant_id), unlike the camelCase signed payload; a camelCase document is rejected at construction rather than silently mis-signed.

The firewall

Byte-identity with the Rust and Python clients is locked by two vendored cross-language vectors, operator_pop_v1.json and pathquery_v1.json. CI proves Rust output == Python output == TypeScript output == vector, byte-for-byte. A mismatch is a hard blocker.

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

Contract: RFC 8785 (JCS) over the camelCase RequestAuthPayload → SHA-256 → 32-byte digest → pure Ed25519 over that digest as the raw message → raw 64-byte signature, base64 in x-synthera-signature.

Timestamps are whole-second RFC 3339 …Z — the chrono-serde fixed point, so the client's signed timestamp and the server's recomputation are the same string.

License

Apache-2.0. See LICENSE.