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

xsign

v1.0.0

Published

Sign and validate XML documents against the SERPRO Integra Contador XMLDSig profile, extensible to other documents via typed templates.

Readme

xsign

Sign and validate XML documents against the SERPRO Integra Contador XMLDSig profile — with typed, opt-in templates for known document shapes, starting with the AUTENTICAPROCURADOR authorization term.

Install

npm install xsign

Node.js 20 or later.

Quick start

import { readFile, writeFile } from "node:fs/promises";
import { Credential, XmlSigner, serproXmldsig } from "xsign";

const xml = await readFile("term.xml", "utf8");
const pfx = await readFile("certificate.pfx");
const credential = Credential.fromPkcs12(pfx, process.env["PFX_PASSWORD"] ?? "");

const signed = new XmlSigner(serproXmldsig).sign(xml, credential);

await writeFile("term-signed.xml", signed.xml);

sign throws CertificateExpiredError for a certificate outside its validity window, InvalidDocumentError if xml already contains a <Signature>, or a parse error for malformed XML. Every error this library throws extends XsignError and carries a stable code:

import { Credential, XsignError } from "xsign";

declare const pfx: Uint8Array;
declare const password: string;

try {
  Credential.fromPkcs12(pfx, password);
} catch (error) {
  if (error instanceof XsignError) {
    console.error(error.code, error.message);
  }
}

Verify a signature

import { SignatureValidator, serproXmldsig } from "xsign";

declare const signedXml: string;

new SignatureValidator(serproXmldsig).validate(signedXml);
// Throws ProfileViolationError or SignatureVerificationError; returns nothing on success.

validate checks both that the XMLDSig structure matches the profile (canonicalization, signature and digest algorithms, transforms, reference URI, certificate count) and that the signature is cryptographically valid against the document.

Templates

A template is a typed, reusable description of one known document: its root element, the signature profile it must be signed with, and a render function that builds well-formed, escaped XML from typed input. xsign ships one, for SERPRO's Integra Contador AUTENTICAPROCURADOR authorization term, importable from the xsign/templates subpath so importing the core xsign package never pulls in template code.

import { Credential, XmlSigner } from "xsign";
import { templates, type TemplateInput } from "xsign/templates";

const input: TemplateInput<"serpro/autentica-procurador"> = {
  signedOn: "20260101",
  validUntil: "20270101",
  recipient: { document: "00000000000191", name: "ACME CONTABILIDADE LTDA", kind: "PJ" },
  author: { document: "11111111000191", name: "JOHN DOE", kind: "PF" },
};

const template = templates["serpro/autentica-procurador"];
const xml = template.render(input);

declare const credential: Credential;
const signed = new XmlSigner(template.profile).sign(xml, credential);
void signed;

template.assertMatches(xml) throws InvalidDocumentError when xml's root element doesn't match the template — useful to validate an externally-produced document before signing it. TemplateId is derived from the registry's own keys, so an unknown id is a compile error, and TemplateInput<Id> is derived from that template's own render signature — neither is hand-maintained. Adding a template is a new file plus one line in the registry; see docs/templates.md and CONTRIBUTING.md.

API reference

xsign

| Export | Kind | What it is | |---|---|---| | Credential | class | An RSA private key paired with its X.509 certificate. Credential.fromPkcs12(pkcs12, password) and Credential.fromPem({ privateKey, certificate }). | | PemCredential | type | Input shape for Credential.fromPem. | | CertificateInfo | class | Parsed certificate metadata: subject, issuer, fingerprint, validity window, key type. CertificateInfo.parse(pem). | | XmlSigner | class | new XmlSigner(profile).sign(xml, credential) → SignedDocument. | | SignatureValidator | class | new SignatureValidator(profile).validate(xml). | | SignedDocument | class | The signed XML plus .toBase64(). | | SignatureProfile | type | The XMLDSig shape a document must be signed with: canonicalization, signature and digest algorithms, transforms, reference URI, certificate count. | | serproXmldsig | const | The SignatureProfile SERPRO's Integra Contador requires — see docs/serpro-profile.md for what each field enforces and why. | | XsignError | class | Abstract base of every error this library throws. Never thrown directly; always carries a stable code. | | InvalidCredentialError | class | A PKCS#12 bundle couldn't be opened, or its key and certificate don't pair. | | CertificateExpiredError | class | The certificate is outside its validity window at signing time. | | UnsupportedKeyError | class | The certificate's key algorithm isn't RSA. | | InvalidDocumentError | class | The XML is malformed, has an unexpected shape, or is already signed. | | ProfileViolationError | class | A signed document's XMLDSig structure doesn't match the required profile. | | SignatureVerificationError | class | The signature failed cryptographic verification. |

xsign/templates

| Export | Kind | What it is | |---|---|---| | templates | const | Every registered template, keyed by stable id. Frozen. | | TemplateId | type | keyof typeof templates — derived, so an unknown id doesn't compile. | | TemplateInput<Id> | type | The typed input a given template's render expects — derived from that template's own signature. |

CLI

A thin shell over the same public API — everything it does, the library also does.

xsign --xml term.xml --pfx certificate.pfx
xsign --xml term.xml --pfx certificate.pfx --out signed.xml --template serpro/autentica-procurador
xsign --verify signed.xml

| Flag | Effect | |---|---| | --xml <path> | Document to sign. | | --pfx <path> | A1 certificate, .pfx / .p12. | | --out <path> | Output path. Default: <xml path>-signed.xml. | | --base64 / --no-base64 | Also write <out>.base64.txt. On by default. | | --password-stdin | Read the PFX password from stdin instead of prompting. | | --template <id> | Validate the XML against a known template before signing. | | --verify <path> | Verify an already-signed XML. Signs nothing. | | --help | Show usage. |

The XML (and, when --template is given, its root element) is always read and validated before the password is requested — a typo'd path shouldn't cost you a password entry.

The PFX password

Three ways to supply it, in this order of precedence:

| # | How | For | |---|---|---| | 1 | --password-stdin | Automation. cat secret \| xsign --password-stdin ... | | 2 | PFX_PASSWORD environment variable | CI/CD and containers, where the secret already arrives via env. | | 3 | Interactive, no-echo prompt | Default when neither of the above is set — fails immediately, instead of hanging, when there's no TTY to prompt on either. |

There is no --password flag, on purpose: a password in a command-line argument leaks through both the shell history and the process list (any user on the machine can read it with ps aux while the command runs). It isn't missing by oversight; it will not be added.

Security notes on the PFX

  • A .pfx / .p12 file contains a private key. Treat it like any other secret: never commit it. This repo's own .gitignore already excludes *.pfx and *.p12.
  • The library API never reads an environment variable, opens a prompt, or touches a file for the password — it takes a string and nothing else. Where that string comes from (a secrets manager, a prompt, an env var) is entirely the caller's decision.
  • string in JavaScript is immutable, so a password cannot be scrubbed from memory after use — it lives until the garbage collector gets to it. That's true of any JS signing library, not something specific to xsign. If your threat model includes a process heap dump, the key belongs in an HSM, not in this package.
  • A successful signature says less than it looks like. See docs/what-signing-proves.md for exactly what xsign proves (integrity, key possession, profile conformance) and what it deliberately leaves out (chain trust, revocation, timestamping).

Contributing

Issues and PRs are welcome. See CONTRIBUTING.md for the dev setup, the test-driven workflow this project is written under, and how to add a new template.

Changelog

See CHANGELOG.md.

License

MIT