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

pkinative

v1.0.0

Published

Zero-dependency native PKI toolkit: strict ASN.1 DER (BER on request), PEM, OIDs and complete RFC 5280 X.509 certificate parsing with every standard extension, CWE-tagged resource limits, stable machine-readable error codes and a conformance gate over x50

Readme

pkinative

Read, verify and build the certificates your software trusts — strictly, safely, on every runtime, without a single dependency.

Zero runtime dependencies TypeScript strict mode Conformance: x509-limbo, Wycheproof and NIST PKITS License: MIT

Zero runtime dependencies. 100% TypeScript. One API for every runtime with Web Crypto: tested in CI on Node.js 22 on Linux, Windows and macOS and on Node.js 24 on Linux, with a Deno, a Bun and a headless Chromium smoke test, and on Node.js 26 (Current) in an advisory run until its LTS date; other Web Crypto runtimes, such as Cloudflare Workers, are expected to work and are not tested in CI. The third library of the native family, under the engineering doctrine of pdfnative and zipnative.

Status: 1.0 — stable, on npm. The public API, the error codes and the reason codes follow semantic versioning: a minor release only adds, and a removal or an incompatible change waits for the next major (ROADMAP.md, SECURITY.md §Compatibility promise). Versions below 1.0.0 are git tags only — source snapshots of each milestone, never released on GitHub or npm.

pkinative reads certificates and verifies them the whole way: signatures, RFC 5280 §6 paths, CRL and OCSP revocation, host names and key purposes. It reads, builds and verifies CMS SignedData and RFC 3161 timestamps, opens PKCS#8 keys and PKCS#12 files into non-extractable Web Crypto keys, and creates certificates and certification requests — with Web Crypto doing every signature.

Why pkinative?

The JavaScript ecosystem parses certificates with node-forge, pkijs, asn1js and @peculiar/x509 — tens of millions of weekly downloads between them, pre-ES2015 code or dependency chains, and a history of ASN.1 parser advisories. pkinative starts from the other end:

  • Strict by default. DER's length, tag, constructed-string, BOOLEAN, INTEGER and BIT STRING rules (X.690 §8, §10, §11.1–11.2.1) are refused, not guessed at: two encodings of one value is an ambiguity an attacker can exploit. The departures from §11.2.2, §11.5 and §11.6 that real issuers commit — trailing zero bits in a named bit list, an explicit DEFAULT, an unsorted SET OF — are diagnostics, refused under strict: true. BER is an explicit option.
  • Safe on hostile input. Every loop runs under a named, CWE-tagged, caller-configurable limit; nesting is iterative; every failure is a typed error with a stable code — never a TypeError.
  • Complete. Every RFC 5280 extension decoded to its ASN.1 module, every GeneralName form, every DirectoryString type, RFC 4514 names, RSA, EC, EdDSA, XDH and ML-DSA keys.
  • Honest about profiles. What real issuers get wrong — a 21-octet serial, an explicit DEFAULT, a non-critical name constraint — is a diagnostic with its RFC section, not a crash and not silence.
  • Verdicts that explain themselves. A chain, a signed message, a timestamp or a PKCS#12 file comes back as a report carrying every reason it was refused, each a stable code with its clause, never an exception for a problem with the input.
  • No cryptography it should not own. pkinative never implements signing, key generation or arithmetic on secret material; every signature is created and verified through Web Crypto, with your key.
  • Held to external corpora. A blocking conformance gate runs x509-limbo, Wycheproof and NIST PKITS, pinned by commit and checksum, holds the parser to the pinned text of RFC 5280 clause by clause, and cross-checks the serial number, validity, CA flag and fingerprint of every parsed certificate against OpenSSL.
  • Agent-pilotable. Machine-readable error codes, a diagnostics channel, llms.txt, a generated API manifest, and a human-in-the-loop AI governance policy.

How it compares

Registry facts only, read on 2026-09-19 (docs/data/comparison-2026-09-19.json).

| Library | Latest | Runtime dependencies | Types bundled | ES modules | Scope | |---|---|---|---|---|---| | pkinative | 1.0 (npm) | 0 | yes | yes | ASN.1, PEM, OIDs, X.509 and PKCS#10 requests, path validation with CRL and OCSP, CMS and RFC 3161, PKCS#8 and PKCS#12 under PBES2; every signature through Web Crypto | | node-forge | 1.4.0 | 0 | no | no | Broad: ASN.1, X.509, TLS, its own RSA and ciphers in JavaScript | | asn1js | 3.0.10 | 3 | yes | yes | ASN.1 BER/DER codec | | @peculiar/x509 | 2.1.0 | 11 | yes | yes | X.509 over Web Crypto, on the @peculiar/asn1 schema stack | | pkijs | 3.4.0 | 6 | yes | yes | Broad PKI over asn1js and Web Crypto: X.509, CRL, OCSP, CMS, timestamps | | jsrsasign | 11.1.5 | 0 | no | no | Broad: ASN.1, X.509, JWS, its own RSA and ECDSA in JavaScript | | micro509 | 0.14.0 | 0 | yes | yes | Small X.509 toolkit, pre-1.0 |

Choose pkinative for strict, dependency-free PKI with every refusal explained by a stable code; choose another tool for what it will never do — legacy PKCS#12, DSA, network fetching, key generation — each a recorded decision (choose guide).

Installation

pkinative is on npm, published by publish.yml from the tagged commit, after the full gate, with npm provenance:

npm install pkinative
npm audit signatures   # optional: verify the registry signatures and provenance of what you installed

Every GitHub release also carries that same tarball, fetched back from the registry, and three SBOMs — CycloneDX and SPDX for the package, CycloneDX for the toolchain that built it — all attested with Sigstore build provenance:

npm install https://github.com/Nizoka/pkinative/releases/download/v1.0.0/pkinative-1.0.0.tgz
gh attestation verify pkinative-1.0.0.tgz --repo Nizoka/pkinative   # optional: check where it was built

A plain git install (github:Nizoka/pkinative#v1.0.0) does not work: dist/ is not committed. Every runtime runs the same build; the package has browser, import and require conditions and no runtime dependency.

Quick start

Read every certificate of a PEM text and print what a person checks first:

import { computeFingerprint, decodePem, formatDistinguishedName, formatFingerprint, getExtension, parseCertificate } from 'pkinative';

export function describeCertificates(pemText: string): string[] {
    const lines: string[] = [];
    for (const { bytes } of decodePem(pemText, { label: 'CERTIFICATE' })) {
        const cert = parseCertificate(bytes);
        const names = getExtension(cert, 'subjectAltName')?.names ?? [];
        const iso = (ms: number): string => new Date(ms).toISOString();
        lines.push(
            `subject: ${formatDistinguishedName(cert.subject)}`,
            `issuer:  ${formatDistinguishedName(cert.issuer)}`,
            `valid:   ${iso(cert.validity.notBefore.epochMilliseconds)} → ${iso(cert.validity.notAfter.epochMilliseconds)}`,
            `names:   ${names.map((n) => (n.kind === 'dNSName' ? n.value : n.kind)).join(', ')}`,
            `sha-256: ${formatFingerprint(computeFingerprint(bytes, 'SHA-256'))}`,
        );
    }
    return lines;
}

This block is recipes/quick-start.ts, executed on every test run against the letsencrypt.org certificate. The quick start guide goes further: extensions, diagnostics, strict, limits and errors; the use cases go the whole way, from a chain verdict to a timestamped signature. Every public export, with its signature and the errors it throws, is listed in docs/assets/api.json.

What you get

| Area | Exports | |---|---| | The one-call verdict | verifyCertificateChain — path building, RFC 5280 §6 validation, host name, key purpose and revocation in one report that lists every reason at once (use cases) | | Certificates | parseCertificate, getExtension, decodeExtensionValue, formatDistinguishedName — every RFC 5280 field and standard extension | | Certification requests | parseCertificationRequest (PKCS#10, a CertificationRequest: subject, key, attributes and the extensions it asks for, read with the certificate's own readers), verifyCertificationRequest — the proof of possession checked with the key inside the request, as a VerifyCertificationRequestReport that never throws for bad bytes (VerifyCertificationRequestOptions for allowSha1 and the reading options) | | Signature verification | verifyCertificateSignature, verifySelfSignature, canVerify — one signature against one issuer key, through Web Crypto; PkiCryptoError when it could not be checked, never when it failed | | Creation | createCertificate, createCertificationRequest (PKCS#10), canSign — signed by a Web Crypto key or an ExternalSigner; the structural encoders encodeDistinguishedName, encodeExtensions, encodeSubjectAltName, encodeKeyUsage, KEY_USAGE_BITS, encodeSubjectPublicKeyInfo and the rest | | Paths | buildCertificatePath, validateCertificatePath — RFC 5280 §6 with name constraints and the policy tree | | Host names and purposes | checkServerName, matchDnsName (RFC 6125), checkExtendedKeyUsage, KEY_PURPOSES, ANY_EXTENDED_KEY_USAGE | | Revocation | CRLs: parseCertificateList, findRevocation, verifyCrlSignature, checkRevocation (delta lists and scopes included); OCSP: createOcspRequest, encodeOcspCertId, parseOcspResponse, verifyOcspSignature, checkOcspStatus, OCSP_NONCE_OID — you fetch, pkinative judges | | CMS and timestamps | createSignedData (a Web Crypto key or an ExternalSigner such as an HSM), verifySignedData, parseSignedData, verifySignerInfoSignature, addUnsignedAttribute; RFC 3161 createTimeStampRequest, parseTimeStampResponse, parseTimeStampToken, parseTstInfo, verifyTimeStampToken, addTimeStampToken; PkiCmsError (use cases) | | Private keys and PKCS#12 | openPkcs12 (one call: MAC, SafeContents, certificates, keys), parsePkcs12, verifyPkcs12Mac, openSafeContents; PKCS#8 parsePrivateKeyInfo, parseEncryptedPrivateKeyInfo, importPrivateKey, decryptPrivateKey — keys unwrapped into non-extractable Web Crypto handles, PBES2 only; canDecrypt, PkiKeyError (use cases) | | PEM | decodePem (strict or lax RFC 7468, optionally restricted to one label), encodePem | | ASN.1 | decodeAsn1, decodeAsn1Sequence; the typed readers readBoolean, readInteger, readSmallInteger, readEnumerated, readNull, readBitString, readOctetString, readObjectIdentifier, readRelativeOid, readString (eight string types) and readTime (both time types); DER encoders (encodeSequence, encodeInteger, encodeRelativeOid, encodeTlv, …); encodeAsn1Node (byte-identical re-encoding) | | OIDs | encodeOid, decodeOid, isValidOid, getOidName over a registry of 300+ names | | Fingerprints, key identifiers and hashing | computeFingerprint, computeFingerprintAsync (Web Crypto), formatFingerprint, computeKeyIdentifier; shake256 (FIPS 202, over public data — the digest an Ed448 CMS signer uses) | | Errors and limits | PkiError and its six subclasses — PkiEncodingError, PkiCertificateError, PkiLimitError, PkiCryptoError, PkiCmsError, PkiKeyError — each with a stable code (error guide); DEFAULT_PKI_LIMITS |

pkinative has 294 public exports. There is deliberately no PEM-to-certificate shortcut: decodePem and parseCertificate compose, as Go's encoding/pem and crypto/x509 do, so the certificate parser carries no PEM code (recipes/pem-bundle.ts).

Security model

Every certificate, PEM text and DER blob is attacker-controlled. Twenty-two named limits (maxDepth, maxNodes, maxExtensions, …) bound every loop, each with its CWE; structural failures throw a PkiError subclass with a stable code, conformance concerns go to a diagnostics channel (onDiagnostic, or strict: true to refuse them), and a judgement — a chain, a message, a timestamp, a key file — returns its reasons in a report. No eval, no I/O, no dynamic import in the engine, and no secret-dependent cryptography in TypeScript. Details: SECURITY.md and the security guide.

Conformance

A blocking gate (conformance guide) runs the built package over corpora pinned by commit and SHA-256, levels L0 to L8:

  • x509-limbo — all 30 361 unique x509-limbo certificates parse, or are refused only where every limbo case using them expects failure; 565 certificates refused, each held to a reviewed baseline. Every certificate re-encodes byte for byte, every parsed one agrees with OpenSSL on serial, validity, CA flag and fingerprint, and every limbo path-validation case is scored against the verdict the corpus expects.
  • RFC 5280, clause by clause — the requirement sentences of §4.1 and §4.2 in the pinned RFC text, each held by a clause pkinative must diagnose or excluded with a written reason.
  • NIST PKITS — the 405 certificates and 173 CRLs all parse; 195 of the 203 scored paths agree with NIST, and the 224 signed S/MIME messages are verified whole, each verdict equal to its signer's path. Every disagreement is a reviewed, written deviation.
  • Wycheproof — all 1 530 Wycheproof ECDSA vectors on P-256, P-384 and P-521: every valid signature decodes, every encoding defect is refused.

What this is and is not evidence of, standard by standard, is the standards self-assessment.

Known limitations

  • No external security audit (ADR 0010). What stands in its place — the adversarial release audit, the conformance gate, 100 % coverage, the seeded adversarial suites — is listed in SECURITY.md.
  • Coverage-guided fuzzing has not run on GitHub yet. The ClusterFuzzLite workflow is in place; its first run needs the published repository (SECURITY.md). The seeded adversarial suites run in every gate.
  • An id-RSASSA-PSS private key under PBES2 depends on the host: a pkcs8ShroudedKeyBag or an encrypted PKCS#8 is unwrapped by Web Crypto and never handed back to re-wrap, so on Node.js 22 it stays PKI_REASON_PKCS12_KEY_UNSUPPORTED or PKI_CRYPTO_KEY_UNSUPPORTED — re-export it under rsaEncryption with the tool that holds it. Public keys, unencrypted PKCS#8 and plain keyBags under such a certificate verify and open everywhere.
  • No built-in trust store, no fetching. The caller supplies the trust anchors, and the CRLs and OCSP responses a check needs: pkinative does no network I/O (ADR 0006).
  • No OCSP-request or timestamp-request reader. createOcspRequest and createTimeStampRequest write them; no public function parses one. A PKCS#10 request is read by parseCertificationRequest and judged by verifyCertificationRequest.
  • No CRL or OCSP-response writer. pkinative reads and verifies both, and writes neither.
  • ML-DSA signatures are not verified, though ML-DSA keys are read; DSA signers are not verified (ADR 0004); an Ed448 CMS signer is verified where the host verifies Ed448 (Node.js) and reported PKI_REASON_SIGNATURE_NOT_CHECKED where it does not (Bun, Chromium) (ADR 0022).
  • Internationalized names are not converted: a non-ASCII octet in an IA5String name is refused, not guessed, and IDNA is not applied. No public suffix list is embedded: a wildcard needs three labels, and registry-level policy is the caller's.
  • X.520 attribute syntaxes are diagnosed, not enforced, when reading a name: a countryName that is not a two-character PrintableString, or an emailAddress that is not an IA5String, is read with a warning (refused under strict: true); a value past its RFC 5280 Appendix A.1 upper bound, or a country code outside ISO 3166-1, is read as it is with an info (PKI_DIAG_NAME_ATTRIBUTE_TOO_LONG, PKI_DIAG_NAME_COUNTRY_UNKNOWN). TeletexString is read as Latin-1, with a diagnostic.
  • The Certificate Transparency SCT list is kept in its TLS encoding, neither decoded nor verified.
  • The SHA implementations are synchronous TypeScript over public data; computeFingerprintAsync uses Web Crypto when the host has it.
  • Attribute certificates (ITU-T X.509 §12 and RFC 5755) are not read; the scope and priority are determined when a consumer requests one.
  • Refusals by design — PKCS#12 and PKCS#8 under PBES2 only, no DSA verification, no network fetching, no key generation or export, no PKCS#8 or PKCS#12 writer, no ETSI long-term (B-LTA) signature formats — are recorded decisions, listed below.

What pkinative will NOT do

No runtime dependency. No TypeScript implementation of signing, key generation or modular arithmetic on secrets. No filesystem, network or process access inside the engine. No lenient decoder that silently accepts what the standard forbids. No PEM parsing inside the certificate parser. The larger refusals, and why each is a decision rather than a gap, are recorded as architecture decision records in docs/adr/.

Ecosystem

  • pdfnative — the mother project; its PAdES and LTV signature stack is where pkinative comes from, and adopting pkinative there is pdfnative's own milestone.
  • zipnative — the sibling whose error vocabulary, limits and conformance-gate patterns pkinative inherits.

Development

npm ci --ignore-scripts
npm run gate:fast        # typecheck, lint, tests, documentation checks
npm run gate             # the CI profile
npm run conformance:fetch && npx tsx scripts/gate.ts --publish --require-all

CONTRIBUTING.md has the conventions and the release procedure; AGENTS.md is the brief for AI coding agents, who work under a human-in-the-loop policy (.github/AGENT_RULES.md).

Origin

pkinative grew out of the X.509 and CMS code pdfnative wrote for PDF signatures. An audit found that code useful but not reusable as it stood — a signed-shift length bug, recursion without a bound, extensions silently lost after an issuerUniqueID, and pure-JavaScript RSA and ECDSA that are not constant-time. pkinative keeps the ideas, rewrites the parser, and leaves the secret-dependent arithmetic to Web Crypto.

License

MIT © Nizoka — Plika