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

@charter-agreement-protocol/signer

v0.2.2

Published

Holder-side companion signer for the Charter Agreement Protocol (CAP) in TypeScript - key custody only: an atomic key snapshot, the sign callback, the wrong-key guard, and post-sign verification, with framing, the claims gate, the R1-R3 honest-signer refu

Readme

@charter-agreement-protocol/signer

Holder-side companion signer for the Charter Agreement Protocol (CAP) — the TypeScript sibling of the Elixir charter_agreement_signer.

CAP verifies; it never authorizes. This signer signs; it never takes custody. You keep the key (HSM, OS keychain, key server) behind a handle object with two callbacks; the signer resolves ONE atomic keyIdentity snapshot, then delegates EVERYTHING pure to the verifier package's producer surface — the exact RFC 7515 signing input, the producer claims gate, the R1–R3 honest-signer refusal guards (claims-truth, no-equivocation, ancestry/governing coverage), and assembly — before the key signs, with exactly one implementation. What stays here is custody: the sign callback, the wrong-key guard against the snapshot's public key, and the post-sign verification of the assembled artifact through the @charter-agreement-protocol/verifier package.

Install

npm install @charter-agreement-protocol/signer

Release 0.2.2 requires Node >= 24.8.0 (ML-DSA support in the Node builtins) and depends on @charter-agreement-protocol/verifier ^0.5.0.

Quickstart

This runnable Ed25519 example keeps the private key in a Node KeyObject. For an HSM, OS keychain, or remote key server, replace the handle's callbacks with your custodian's key identity and signing operations.

import { signDescriptor } from "@charter-agreement-protocol/signer";
import { generateKeyPairSync, sign as nodeSign } from "node:crypto";

const { publicKey, privateKey } = generateKeyPairSync("ed25519");
const publicKeyBase64url = publicKey.export({ type: "spki", format: "der" }).subarray(-32).toString("base64url");

const handle = {
  // ONE atomic snapshot — the kid and public key cannot disagree.
  keyIdentity() {
    return { kid: "my-key-001", publicKey: publicKeyBase64url }; // base64url raw public key
  },
  // The holder's job: sign the exact message bytes with the held key.
  sign(message) {
    return nodeSign(null, message, privateKey); // Buffer is a Uint8Array
  },
};

const result = await signDescriptor(
  {
    protocol_revision: 2,
    descriptor_number: 1,
    verification_keys: [
      { key_id: "my-key-001", algorithm: "Ed25519", public_key: publicKeyBase64url, status: "active" },
    ],
    attestation_hints: [],
    extensions: { critical: {}, optional: {} },
    effective_from: new Date().toISOString(),
  },
  handle,
);

if (!result.ok) throw new Error(JSON.stringify(result));
console.log(result.result.descriptor); // Post-verified compact JWS

For ML-DSA-65 artifacts pass { algorithm: "ML-DSA-65" } as the options argument with protocol_revision: 3 claims (pure ML-DSA per RFC 9964, the empty context — your handle signs the exact message bytes as with Ed25519).

API

| Export | What it does | |---|---| | signDescriptor(claims, handle, opts?) | Signs one Party Descriptor; post-verified via verifyDescriptor | | signReceipt(claims, handle, chain, opts?) | Signs one Receipt against the required issuing view; post-verified via verifyReceipt (the reference takes a context, never none) | | signAcceptance(claims, handle, view, opts?) | Signs one Acceptance against the caller's verified view — the R1–R3 refusal guards run over view.chain before the key signs | | signTermination(claims, handle, view, opts?) | Signs one Termination against the caller's verified view — the R1–R3 refusal guards run over view.chain before the key signs |

The acceptance/termination view is { revisionText, descriptorCompacts, chain }: the revision the claims name, the signing party's descriptor chain, and the caller's full ChainView ({ revisions, acceptances, descriptors, terminations }) the refusal guards verify and bind against.

Errors are closed: { ok: false, error: "invalid_handle" \| "signing_failed" \| "invalid_input" \| "refused" \| "verification_failed" } — code rides only on invalid_input and refused; verification_failed is bare (reference parity). "refused" is the honest-signer refusal: the claims contradict the caller's own verified view, and the handle's sign callback is never reached (the atomic keyIdentity snapshot resolves first — the reference ordering). "verification_failed" is the separate post-sign discipline: the assembled artifact did not verify against the caller's view (for example a key that is not in the party descriptor's verification_keys). Malformed claims are rejected pre-sign as "invalid_input" through the verifier package's producer claims gate — they never burn a key operation. A signing failure is never a silent pass.

The sibling verifier

This package is one of two independent TypeScript siblings implementing the Charter Agreement Protocol — no Elixir code or dependency at runtime; the Elixir reference is the certification oracle (the verifier's vendored corpus is certified against it, and its CI cross-verifies TypeScript-signed artifacts from raw bytes).

  • This package — @charter-agreement-protocol/signer: key custody, the wrong-key guard, post-sign verification, and the closed error vocabulary — all protocol logic delegated to the verifier.
  • @charter-agreement-protocol/verifier — verification and the pure producer surface (signing inputs, claims gate, refusal guards, assembly) that this package signs through.

This signer depends on the verifier (^0.5.0, locked to 0.5.0). When changing that dependency, publish the verifier version first, then lock and release the signer against the published version. The packages have independent SemVer versions; signer documentation and build-tool updates can release without a verifier version change.

Evidence

  • The Elixir reference repository's gate verifies TypeScript-signed artifacts from raw bytes (cross-implementation agreement, enforced in CI).
  • Framing, the producer claims gate, refusal guards, and assembly are the verifier package's producer surface (0.5.0) — the same single implementation the independent verifier certifies. What remains here is custody plus input policing: the issuing-view discipline composes the verifier's own verifyChain, never a local reimplementation.
  • Post-sign verification goes through the @charter-agreement-protocol/verifier package — the certified dual-implementation-verified verifier.

The verify side

This package tracks the published verify line: it depends on @charter-agreement-protocol/verifier ^0.5.0. Verifier 0.5.0 added consumer-side surface only; the producer surface this package uses is unchanged. The producer-side capability alignment (mirrored capabilities() with refuse-before-mint, declared minting metadata, profile-bound signing) is the next feature release on this line.

SemVer

Package SemVer decoupled from the protocol's protocol_revision. While the package is below 1.0, breaking public-API changes land in minor versions and are named in the commit message (0.2.0 is one: signTermination's view requires chain, signAcceptance now enforces the refusal guards over the chain it previously ignored, and signReceipt requires its issuing view); at or above 1.0 a package major is owed when a shipped public API is removed or changes behavior.

Development

Use Node 24 (at least 24.8.0) and npm. The lockfile selects TypeScript 7.0.2 and @types/node 26.6.4. CI runs on Linux with Node 24 and a frozen install; the same npm commands are used for local development on macOS and Linux.

npm ci
npm run typecheck
npm test
npm run build
npm run typecheck:web
npm run build:site
npm pack --dry-run

build emits JavaScript and declarations into dist/; build:site writes the browser workshop to site-dist/. prepack runs the library build. For the TypeScript 7 upgrade, the four library output files were byte-identical to the TypeScript 5.9.3 build (observed October 6, 2026 with diff -ru).

Release staging

The Release workflow is the npm trusted publisher for this repository. Prepare a release by updating package.json, package-lock.json, this README's release version, the CHANGELOG, and the workshop's version badge. Run the development checks above, commit and push main, and wait for CI to pass. Then tag that same commit v<package-version> and push that specific tag.

The tag triggers verification and npm stage publish --provenance through GitHub Actions OIDC. A successful staging step prints the package version and stage ID. The package becomes public only after the owner approves the staged release on npmjs.com with 2FA. Keep npm staging in this workflow.

License

Apache-2.0 — see LICENSE and NOTICE.