Strict by default
DER admits one encoding per value, and every X.690 §10–11 violation throws. Two encodings of one value is how a signature covers bytes two parsers read differently. BER is an explicit opt-in.
Parse, verify and build X.509: strict DER, complete RFC 5280, §6 path validation with CRL and OCSP, CMS and RFC 3161 timestamps, PKCS#8 and PKCS#12 — every signature through Web Crypto, zero runtime dependencies, on Node, browsers, Deno, Bun and Workers.
npm install pkinative
npm install --global pkinative-cli
npx -y pkinative-mcp
On npm with provenance since 1.0.0, under semantic versioning: a minor release only adds.
A reader for hostile bytes and a verifier that owns no secret arithmetic — complete on the profile it claims and explicit about the profile it does not.
DER admits one encoding per value, and every X.690 §10–11 violation throws. Two encodings of one value is how a signature covers bytes two parsers read differently. BER is an explicit opt-in.
Nothing to audit but this. No node: import, no process, no filesystem, no network — checked on the shipped bundle. The same build runs on Node, browsers, Deno, Bun and Workers, and tree-shakes: the OID name registry never enters a parser's bundle.
Every loop over untrusted bytes consults a named, CWE-tagged limit. The decoder is iterative, so nesting depth ends in a typed error and never in a stack overflow.
Every field and all nineteen extension kinds — the seventeen of RFC 5280, OCSP no-check and the SCT list — decoded and typed — not a subset with the awkward ones left as raw bytes. Certificates and PKCS#10 requests are read, verified and built with the same readers, codes and bounds.
Certificates, certification requests, CRLs, OCSP responses, CMS signers and timestamps are verified by one call into the host's Web Crypto — RSA, ECDSA, Ed25519, and Ed448 where the host has it. No signing, no key generation, no arithmetic on secret material in TypeScript; hashing, SHAKE256 included, covers public data only.
RFC 5280 §6 path validation — name constraints, policies, key usage — plus CRL and OCSP. verifyCertificateChain composes them and answers with stable reason codes, not exceptions.
RFC 5652 SignedData and RFC 3161 timestamps, created and verified; PKCS#8 and PKCS#12 under PBES2, opened into non-extractable Web Crypto keys.
A structural violation throws; a profile deviation real issuers commit becomes a diagnostic carrying its RFC clause. Never both, and never silence.
Stable machine-readable error and reason codes, a diagnostics channel, llms.txt, a generated API manifest and gate rules that prove an agent can use every export from the docs alone — and an MCP server that hands an assistant 34 typed tools.
One zero-dependency engine, three ways to call it — the verdicts and the codes are the same, so the choice is about who is calling. The capability × surface matrix →
pkinative v1.0.0
You write application code — Node.js, a browser, Deno, Bun or a Worker. The whole surface: readers, writers, path validation, revocation, CMS and timestamps, PKCS#8 and PKCS#12, every signature through Web Crypto, 57 error codes and a verdict for every refusal.
npm install pkinativepkinative-cli v1.0.0 · binary pkinative
You drive a shell, a CI job or a container. The pkinative-cli command has 18 commands and 53 invocations, a --json envelope whose 13 error classes carry the PKI_* code verbatim, exit codes 0, 1 and 2, JSON Schemas and shell completion. Offline, and never a password on the command line.
npm install --global pkinative-clipkinative-mcp v1.0.0
You give an AI assistant tools — Claude Desktop, Claude Code, Cursor, VS Code. The MCP server registers 34 tools with strict schemas and seven prompts, serves sandboxed file resources over stdio or loopback Streamable HTTP, returns a negative verdict as data, and makes no network call.
npx -y pkinative-mcpEvery example below is an executable recipe, run and asserted on every test run.
import { decodePem, formatDistinguishedName, getExtension, parseCertificate } from 'pkinative';
for (const { bytes } of decodePem(pemText, { label: 'CERTIFICATE' })) {
const cert = parseCertificate(bytes);
console.log(formatDistinguishedName(cert.subject));
console.log(new Date(cert.validity.notAfter.epochMilliseconds).toISOString());
console.log(getExtension(cert, 'subjectAltName')?.names.length ?? 0);
}
import { decodeExtensionValue, getExtension, parseCertificate } from 'pkinative';
// Nineteen extension kinds, each typed by its `kind`.
const cert = parseCertificate(der);
getExtension(cert, 'basicConstraints')?.cA;
getExtension(cert, 'keyUsage')?.usages;
getExtension(cert, 'nameConstraints')?.permittedSubtrees;
// Or keep them encoded, and decode only what you need.
const lazy = parseCertificate(der, { decodeExtensions: false });
decodeExtensionValue(lazy.extensions[0].oid, lazy.extensions[0].valueDer);
import { parseCertificate, PkiError, PkiLimitError } from 'pkinative';
try {
// A caller that expects small certificates can say so; every other
// limit keeps its default.
parseCertificate(untrusted, { limits: { maxExtensions: 4 } });
} catch (error) {
if (error instanceof PkiLimitError) console.error(error.code, error.limit);
else if (error instanceof PkiError) console.error(error.code);
else throw error; // a TypeError escaping the parser would be a bug
}
Every export named here is in api.json; the runnable sources are in llms-recipes.txt.
Registry facts and documented scope — no benchmark of someone else's library, no opinion about their code.
| Library | pkinative | node-forge | asn1js | @peculiar/x509 | pkijs | jsrsasign | micro509 |
|---|---|---|---|---|---|---|---|
| Runtime dependencies | 0 | 0 | 3 | 11 | 6 | 0 | 0 |
| Types bundled | ✓ | ✗ | ✓ | ✓ | ✓ | ✗ | ✓ |
| ES modules | ✓ | ✗ | ✓ | ✓ | ✓ | ✗ | ✓ |
| Strict DER by default | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| Signature verification | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ |
| Path validation | ✓ | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ |
Measured, with the machine named. Numbers without a run context are not evidence.
Operations per second
Node.js 22.17.0, win32 x64, Intel Core i7-4510U at 2.00 GHz — a 2014 laptop, with other applications open.
A real end-entity certificate with all ten of its extensions decoded takes about 0.17 ms; roughly half of that is
the extensions, which decodeExtensions: false skips. Full table, with the relative error of every row, in
bench/RESULTS.md.
Every failure is a typed error with a stable code you can branch on. Every export, its module, signature and thrown errors are in a generated manifest. Seven gate rules prove an agent can name and use every export from the published documentation alone — not as a claim, as a check that fails.
An agent that should not write code has two doors to the same engine: pkinative-mcp, an MCP server with 34 tools and seven prompts, whose refusals carry the PKI_* code and whose negative verdicts are data; and pkinative-cli, whose --json contract puts one envelope on stderr and the report on stdout. Both run offline and refuse what the library refuses.
pkinative is the third library of the native family, after pdfnative and zipnative. It began as pdfnative's PAdES signature stack, where an ASN.1 decoder that guessed was a liability, and became the library that reads the certificate rather than the one that trusts it.
The same doctrine runs through all three: zero runtime dependencies, one quality gate, conformance against corpora the project did not write, and human review before anything is published.