Home › Guides › pkinative-cli — Command-line reference

pkinative-cli — Command-line reference

pkinative-cli 1.0.0 puts the whole pkinative engine on the command line: 18 commands, offline, with a stable --json contract for scripts, CI and AI agents. It pins pkinative ^1.0.0, its only runtime dependency.

pkinative-cli is a thin, strict front end over pkinative. Every PKI decision — parsing, path validation, revocation, signature verification, CMS, timestamps, key import — is the engine's; the CLI adds what a library deliberately does not do: it reads files and stdin, tells PEM from DER, bounds every read, turns options into flags, writes artefacts safely, renders reports, and maps every failure to one of 13 stable error classes while carrying the engine's PKI_* code verbatim. Its 18 commands reach every runtime export of pkinative 1.0.0 (117: 104 functions, the 7 classes of the PkiError family, 6 constants), and a test in the CLI repository proves it.

Command Subcommands Engine API behind it
pem decode, encode decodePem, encodePem
oid name, encode, decode, validate, list getOidName; encodeOid, encodeObjectIdentifier, encodeRelativeOid; decodeOid, readObjectIdentifier, readRelativeOid; isValidOid; OID_REGISTRY
fingerprint — computeFingerprint, computeFingerprintAsync (--webcrypto), formatFingerprint, computeKeyIdentifier (--key-id), shake256 (--shake256)
asn1 decode, encode decodeAsn1, decodeAsn1Sequence, the eleven read* readers, encodeAsn1Node; the encode* builders (encodeSequence, encodeInteger, encodeTime, …)
cert inspect, create, encode, decode-extension, verify-signature, check-name, match-name, check-purpose parseCertificate, getExtension, formatDistinguishedName; createCertificate; the X.509 encoders (encodeDistinguishedName, encodeSubjectAltName, encodeSignatureAlgorithm, …); decodeExtensionValue; verifyCertificateSignature, verifySelfSignature; checkServerName; matchDnsName; checkExtendedKeyUsage
csr inspect, create, verify parseCertificationRequest, createCertificationRequest, verifyCertificationRequest
chain verify, build, validate verifyCertificateChain; buildCertificatePath; validateCertificatePath, with verifyCertificateSignature on each link
crl inspect, find, verify-signature, check parseCertificateList, findRevocation, verifyCrlSignature, checkRevocation
ocsp request, cert-id, inspect, verify-signature, check createOcspRequest, encodeOcspCertId, parseOcspResponse, verifyOcspSignature, checkOcspStatus
cms sign, verify, inspect, verify-signer, add-attribute, add-timestamp createSignedData, verifySignedData, parseSignedData, verifySignerInfoSignature, addUnsignedAttribute; addTimeStampToken after verifyTimeStampToken
tsp request, inspect, verify createTimeStampRequest; parseTimeStampResponse, parseTimeStampToken, parseTstInfo; verifyTimeStampToken
key inspect, check parsePrivateKeyInfo, parseEncryptedPrivateKeyInfo; importPrivateKey, decryptPrivateKey
p12 inspect, verify-mac, bags, open parsePkcs12, verifyPkcs12Mac, openSafeContents, openPkcs12
doctor — canVerify, canSign, canDecrypt, plus the Node.js and engine version checks
limits — DEFAULT_PKI_LIMITS
explain — the error, reason and diagnostic registries of the pinned pkinative release
schema — — (the CLI's own JSON Schemas, draft 2020-12, and its capability manifest)
completion — — (generated from the same command registry as --help)

That is 53 invocations: 47 subcommands and the 6 commands that take none. The CLI repository's knowledge base maps every engine export to the invocations that reach it.

Installation #

npm install --global pkinative-cli
pkinative doctor

The binary is pkinative. It runs on Node.js ^22.22.2 || ^24.14.1 || >=25.8.2 — the first releases that fix CVE-2026-21713, a variable-time PKCS#12 MAC comparison in Web Crypto — and pkinative doctor checks that floor. The package has exactly one runtime dependency, pkinative ^1.0.0, imported through one module and never inlined into the CLI's own CommonJS binary. It declares Node.js only; its CI also smoke-runs the built CLI under Deno and Bun, as information rather than a promise.

Every release is published from CI with npm provenance, a Sigstore build-provenance attestation and CycloneDX and SPDX SBOMs attached to the GitHub release. To check what you installed:

npm pack pkinative-cli@1.0.0
gh attestation verify pkinative-cli-1.0.0.tgz --repo Nizoka/pkinative-cli

When to use the CLI #

Use the CLI when… Use the library when… Use the MCP server when…
You write shell scripts, Makefiles or CI jobs You build a service in Node.js, Deno, Bun, a browser or a worker A conversational assistant needs PKI tools
Certificates, CRLs and signatures live on disk or arrive through a pipe The bytes live in memory and you call the engine directly The bytes arrive as PEM or base64 in a tool call, or sit in a sandbox directory
You want exit codes, --dry-run and one JSON envelope per run You want the full API, its types and an ExternalSigner You want typed tools with schemas, prompts and resources

The three surfaces share one engine, so a verdict is the same whichever one computes it; Choosing your surface holds the capability × surface table.

Quick start #

# What is this certificate?
pkinative cert inspect server.pem

# Is it valid for this host, now, with revocation?
pkinative chain verify server.pem --untrusted intermediate.pem --trust root.pem \
  --host www.example.com --crl ca.crl --require-revocation

# Issue a self-signed CA, then a leaf under it
pkinative cert create --spec ca.json --key ca.key -o ca.pem
pkinative cert create --spec leaf.json --key ca.key --issuer ca.pem --public-key leaf.csr -o leaf.pem

# Sign and verify a file
pkinative cms sign --content report.pdf --cert signer.pem --key signer.key --detached -o report.p7s
pkinative cms verify report.p7s --content report.pdf --trust root.pem

# Machine-readable everything
pkinative chain verify server.pem --trust root.pem --untrusted ca.pem --json --summary

Every subcommand has a runnable example in the CLI repository's samples/ directory, as a .sh and a .ps1 script, and those are the examples its CI runs and pins.

The agent contract #

The CLI is built to be driven by programs — CI steps, scripts, AI agents — without parsing prose. Everything in this section is stable across 1.x, and fields are only ever added.

Exit Meaning
0 Success; also a closed pipe (EPIPE), which ends the process quietly
1 Any failure but usage: parse, I/O, policy, a limit, a negative verdict (the report is printed first)
2 Usage error (E_USAGE)
130 / 143 Interrupted by SIGINT / SIGTERM; the file being written is removed, finished outputs stay

A status envelope and two error envelopes, as an agent sees them (output of the 1.0.0 binary on the CLI's own test fixtures):

{"ok":true,"command":"chain verify","valid":true,"pathLength":3,"signaturesChecked":true,"diagnostics":[]}
{"ok":false,"command":"cert inspect","error":{"code":"E_INPUT","message":"leaf.key.pem holds no CERTIFICATE or X509 CERTIFICATE or TRUSTED CERTIFICATE PEM block (labels present: PRIVATE KEY).","pkiCode":"PKI_PEM_UNEXPECTED_LABEL","remedy":"pkinative pem decode <file> (lists the labels present)"},"diagnostics":[]}
{"ok":false,"command":"cert inspect","error":{"code":"E_LIMIT","message":"certificate \"leaf.crt.pem\" exceeds --max-input-bytes (100 bytes; observed at least 1005). Raise the bound only for trusted input.","detail":{"limit":"maxInputBytes","flag":"--max-input-bytes","configured":100,"observed":1005},"remedy":"--max-input-bytes <size> (trusted input only)"},"diagnostics":[]}
Envelope field Present Meaning
ok, command always The outcome, and the command with its subcommand ("chain verify")
error.code failures One of the 13 E_* classes — branch on this
error.message failures For people: the CLI's context, then the engine's message
error.pkiCode engine failures pkinative's frozen PKI_* code, verbatim — the exact cause
error.detail when known limit, flag, configured, observed; offset, path, algorithm
error.remedy when a flag or command lifts it The CLI flag that changes the outcome, e.g. --allow-sha1, --ber, --max-depth <value>
error.reasons negative verdicts The engine's PkiReason list in the wire form
diagnostics always Every PkiDiagnostic the engine emitted: { code, severity, message, standard, path, offset? }
config a config file supplied defaults The .pkinativerc.json that did

Command-specific success fields — valid, pathLength, signaturesChecked, output, bytes (the DER length of an artefact), encoding, nonce, count, dryRun, … — are listed per invocation by pkinative schema manifest. Without --json, a failure prints error E_<CLASS> (PKI_<CODE>): <message> on stderr, then a remedy: … line when there is one.

JSON reports use pkinative's wire form: bigint becomes a decimal string, Uint8Array lowercase hex, epochMilliseconds stays a number, absent optional members are omitted and member names are unchanged. The CLI adds nothing to an engine result except where it is a CLI view: key and p12 reports are allow-list views that never carry key bytes, and command-level wrappers ({ blocks }, { oids }, { valid, reasons }) are documented per command.

Token economy. --summary prints the command's minimal JSON shape (codes instead of full reasons, names instead of objects); --fields a,b.c keeps only these dot-paths, a segment on an array mapping over it; an unknown path is omitted, except under an array, where each element it matches nothing in becomes null; --pretty indents; --quiet silences text notes and diagnostics on stderr, while the envelope still carries the diagnostics.

Inputs. A PKI input is a path, - for stdin, or stdin when the path is absent — refused on an interactive terminal. It is PEM when its first non-blank bytes are -----BEGIN , DER otherwise. Where one object is expected, a PEM file with several matching blocks is refused (E_INPUT) rather than reduced to its first block; pem decode --index extracts one. The repeatable inputs (--trust, --untrusted, --chain, --crl, --ocsp) accept PEM bundles. Time is --at <ISO 8601 | epoch ms | now>, UTC unless zoned: pin it for reproducible verdicts.

Parsing rules. Global flags and subcommand flags may sit anywhere on the line; a boolean flag never consumes the next token; --flag=value is the explicit form; a lone - is a value (stdin or stdout); -- ends the flags. Short aliases: -i (--input), -o (--output), -f (--format), -q, -h, -V. Each flag is given once unless the manifest marks it repeatable, and a subcommand takes at most the operands the manifest declares, so a verdict never depends on argument order.

The schema subjects #

pkinative schema (or schema list) lists the twelve subjects; every JSON Schema is draft 2020-12 with an $id versioned by the CLI release.

Subject Kind Prints
manifest document Commands, flags, operands, exit codes, error classes, the wire form, each invocation's outputs and envelope status fields
errors document The E_* classes and the 57 PKI_* → E_* mappings with their remedies
limits document The 22 bounds: flag, kind, effective value
status schema The --json success envelope
error schema The --json failure envelope
asn1-spec schema The asn1 encode node spec
cert-spec schema The cert create spec
csr-spec schema The csr create spec
config schema The .pkinativerc.json file
cert-encode-spec schema The cert encode spec of each structure ($defs/<structure>)
report schema report <command> [<subcommand>]: the --json report on stdout, generated from the TypeScript types and held to every sample
summary schema summary <command> [<subcommand>]: its --summary shape
pkinative schema manifest --pretty
pkinative schema report chain verify
pkinative schema summary cert inspect

Refused combinations #

Usage errors are E_USAGE, exit 2, and they are detected before anything is written — save the one only the input can reveal, p12 open on an RSA key without --rsa-scheme (below). Beyond an unknown flag, a malformed value or a missing required flag, the CLI refuses:

Where Refused
Every command --password, --pass, --passin and --key-password (before anything else runs); --password-stdin=<value>; two password sources at once; the password from stdin when an input is read from stdin too; combined short flags (-qf) and -o=value; a single-value flag given twice, a switch given twice, a flag under both its names; more operands than the invocation takes; an operand beside the flag it stands for; a --max-* value that is zero, not an integer, or beyond Number.MAX_SAFE_INTEGER (there is no "unbounded"); an --at that is not ISO 8601, epoch milliseconds or now, or a date that does not exist; --encoding pem with no PEM label; raw DER to an interactive terminal; a .pkinativerc.json key outside the nine presentation keys; --config naming a file that does not exist
pem decode --output without --index; encode without --label
oid name, encode or validate without an OID; encode with more than one; --relative without --tlv; decode with both, or neither, a hex argument and --input
fingerprint --key-id with --shake256; --webcrypto with --key-id or --shake256
asn1 decode --sequence with --path, --read or --reencode; --read with --reencode; a --path that is not dot-separated indices; encode without --spec
cert inspect --extension with --raw-extensions, or naming a kind outside the 19; create or encode <structure> without --spec; create with no subject key (it is derived from --key only for a self-signed certificate from an unencrypted key); encode without one known structure; encode signature-algorithm with --spec; decode-extension without --oid, or with both or neither of --value and --input; check-name without exactly one of --host and --ip; match-name without two operands; check-purpose without --purpose
Signing (cert create, cert encode signature-algorithm, csr create, cms sign) neither or both of --key and --p12; an RSA key without --rsa-scheme; --rsa-scheme or --salt-length on a non-RSA key; --salt-length with pkcs1; pkcs1 for an id-RSASSA-PSS key; --hash on Ed25519 or Ed448; --hash SHA-1 without --allow-sha1; an encrypted key or a PKCS#12 without a password; an encrypted key whose type nothing names (--key-type, the certificate or the public key)
csr create without --spec, or with no public key (derived from --key only for an unencrypted key)
chain verify, build or validate without --trust; verify with both --host and --ip; validate without a path
crl find without exactly one of --serial and --cert; verify-signature without --issuer; check without --cert
ocsp request or cert-id without --issuer; a nonce outside 1–32 octets (RFC 8954), also for chain verify --ocsp-nonce; check without --cert and --issuer
cms sign without --content or --content-digest, with both, or with --content-digest but no --detached; sign without a signer certificate (--cert, or a --p12 that holds it); verify without --trust; verify-signer without --cert; add-attribute without --attribute; add-timestamp without --token
tsp request with both, or neither, of --data and --digest; verify with both, or neither, of --token and --response, without --trust, or without what was stamped (--request, --data or --digest)
p12 verify-mac, bags or open without a password; open on an RSA key without --rsa-scheme — only the file can reveal it, so the report (PKI_REASON_PKCS12_RSA_SCHEME_UNSPECIFIED, the key not imported) is printed first, then E_USAGE exits 2 with the remedy --rsa-scheme pkcs1|pss, and no --certs-out is written
explain neither a code nor --list
schema an unknown subject; an argument after a subject other than report and summary; report or summary without a known invocation
completion no shell, or a shell other than bash, zsh, fish, powershell (pwsh is accepted for PowerShell)

--config together with --no-config is not refused: --no-config wins and the named file is never read.

Error classes, codes and remedies #

Every failure is one of 13 classes. pkinative's 57 frozen error codes map to them by a fixed table — typed against the engine's code union, so an engine release that added a code would fail the CLI's type-check rather than leak as E_RUNTIME — and the engine's code always rides along, verbatim, in error.pkiCode.

Class Exit PKI_* codes mapped to it When
E_USAGE 2 PKI_INVALID_OPTION, PKI_API_MISUSE, PKI_LIMIT_INVALID A missing, unknown or invalid flag, argument or option value
E_INPUT 1 PKI_INVALID_INPUT, PKI_ASN1_VALUE_OUT_OF_RANGE, PKI_PEM_LABEL_INVALID, PKI_PEM_UNEXPECTED_LABEL, PKI_CMS_CONTENT_TYPE_UNEXPECTED A spec, label or value is unacceptable; a key does not belong to its certificate
E_PARSE 1 the other 35: every remaining PKI_ASN1_*, PKI_OID_INVALID, PKI_PEM_NO_BLOCK, PKI_PEM_LABEL_MISMATCH, PKI_PEM_UNTERMINATED, PKI_PEM_BASE64_INVALID, the ten PKI_X509_*, PKI_CMS_STRUCTURE_INVALID, PKI_KEY_STRUCTURE_INVALID The bytes are not the DER, PEM or JSON they claim to be
E_IO 1 — A filesystem failure, including a refused overwrite (remedy: --overwrite)
E_SECURITY 1 PKI_CRYPTO_ALGORITHM_REFUSED, PKI_KEY_ENCRYPTION_UNSUPPORTED, PKI_KEY_MAC_UNSUPPORTED A policy refusal: SHA-1, a legacy PKCS#12 cipher or MAC, a PBES1 key
E_LIMIT 1 PKI_LIMIT_EXCEEDED A bound was exceeded: one of the 22 pkinative limits, or --max-content-size
E_UNSUPPORTED 1 PKI_PEM_HEADERS_FORBIDDEN, PKI_CRYPTO_ALGORITHM_UNSUPPORTED, PKI_CRYPTO_KEY_UNSUPPORTED, PKI_CMS_VERSION_UNSUPPORTED, PKI_CMS_CONTENT_NOT_OCTET_STRING, PKI_KEY_VERSION_UNSUPPORTED The algorithm, key or structure is not supported
E_CRYPTO 1 PKI_CRYPTO_UNAVAILABLE Web Crypto could not run the operation
E_PASSWORD 1 PKI_CRYPTO_DECRYPTION_FAILED A wrong password, or a MAC that does not match it
E_NOT_FOUND 1 — A named item is absent: an extension, a PEM block, a signer, a code
E_VERIFY_FAILED 1 — A signature, chain, request, timestamp or MAC verdict is negative; error.reasons says why
E_CHECK_FAILED 1 PKI_STRICT_DIAGNOSTIC A check returned reasons (revocation, OCSP, name, purpose), or --strict escalated a warning
E_RUNTIME 1 PKI_INTERNAL Anything else; the CLI's seeded fuzz suite holds that no input reaches it

E_IO, E_NOT_FOUND and E_VERIFY_FAILED carry no engine code: they are the CLI's own (a filesystem error, a missing item, a negative verdict whose reasons are in error.reasons). pkinative schema errors prints the whole 57 → 13 table.

Remedies. The engine's messages name library options that do not exist on a command line, so the envelope adds remedy — the flag or command that lifts the refusal. A code absent from this table has no flag-level remedy: the input itself must change. A few call sites give a remedy of their own where the right advice depends on what was being read (cert decode-extension, a legacy-encrypted PKCS#8 --key), and a limit the CLI enforces before the engine runs names its own flag.

pkiCode remedy
PKI_ASN1_LENGTH_NON_MINIMAL, PKI_ASN1_INDEFINITE_LENGTH_FORBIDDEN, PKI_ASN1_CONSTRUCTED_STRING_FORBIDDEN, PKI_ASN1_BOOLEAN_INVALID --ber (the producer wrote BER, not DER)
PKI_ASN1_TRAILING_DATA Pass exactly one object; asn1 decode --sequence for concatenated objects; --allow-trailing (asn1 decode, cms inspect, cms verify) when the container defines what follows
PKI_PEM_BASE64_INVALID --pem-mode lax (whitespace, line length, headers); a missing = padding must be restored first
PKI_PEM_UNEXPECTED_LABEL pkinative pem decode <file> (lists the labels present)
PKI_STRICT_DIAGNOSTIC Drop --strict, or fix the producer the diagnostic names
PKI_CRYPTO_ALGORITHM_REFUSED --allow-sha1 (only for legacy material you already trust)
PKI_CRYPTO_DECRYPTION_FAILED --password-file <file> | --password-stdin | PKINATIVE_PASSWORD (check the password)
PKI_X509_EXTENSION_MALFORMED cert inspect --raw-extensions (keeps every extension undecoded)
PKI_KEY_ENCRYPTION_UNSUPPORTED From a PKCS#12: convert it once with OpenSSL 3.4 or later (openssl pkcs12 -in legacy.p12 -legacy -aes256 -out bundle.pem, then openssl pkcs12 -export -in bundle.pem -pbmac1_pbkdf2 -out modern.p12). From a PKCS#8 --key: re-encrypt it with PBES2 (openssl pkcs8 -topk8 -v2 aes-256-cbc -v2prf hmacWithSHA256)
PKI_KEY_MAC_UNSUPPORTED --allow-unverified-integrity (p12 open; only for a file whose origin you trust), or re-export it with openssl pkcs12 -export -pbmac1_pbkdf2
PKI_API_MISUSE pkinative <command> --help (the options given are incomplete or inconsistent for this input)
PKI_LIMIT_EXCEEDED --max-<limit> <value>, the flag detail.flag names (pkinative limits lists them); raise a bound for trusted input only

Diagnostics are not errors. The engine's PKI_DIAG_* codes ride in the envelope's diagnostics[] and print as warning PKI_DIAG_… at <path>: <message> (<standard>) lines on stderr unless --quiet. --strict escalates the first warning to E_CHECK_FAILED (pkiCode: PKI_STRICT_DIAGNOSTIC) before any report or artefact is written — including a warning that a verify or check report collects, not only one raised while parsing.

pkinative explain <code> explains any code the CLI can print: the 13 E_* classes, and from the registries of the pinned pkinative release every PKI_* error (with its CLI class, exit code and the flag that lifts it), every PKI_REASON_* reason and every PKI_DIAG_* diagnostic. pkinative explain --list [--kind error|reason|diagnostic|cli] enumerates them. The errors guide explains the engine's three vocabularies, and Where the codes surface compares the library, CLI and MCP views of the same failure.

pkinative explain PKI_CRYPTO_ALGORITHM_REFUSED
pkinative explain E_VERIFY_FAILED --json
pkinative explain --list --kind reason

Command reference #

Eighteen commands in six groups: Encodings (pem, oid, fingerprint, asn1), Certificates (cert, csr), Paths & revocation (chain, crl, ocsp), Signatures & time-stamps (cms, tsp), Keys (key, p12) and Meta (doctor, limits, explain, schema, completion). Every invocation also accepts the global options; the tables below list each subcommand's own flags, and pkinative <command> --help prints the same reference. A flag marked (repeatable) may be given several times; <file> may be - for stdin.

Encodings #

pkinative pem #

RFC 7468 textual encoding. decode lists every block (label, size, offset, headers), or writes one block's bytes with --index; encode wraps DER bytes in one block. The global --pem-mode lax tolerates whitespace, line length and RFC 1421 headers; a missing base64 padding is refused in both modes.

pkinative pem decode [<file>] [--index <n> [--encoding pem|der|hex] [-o <file>]]
pkinative pem encode [<file>] --label <LABEL> [-o <file>]
pem decode Description
--input, -i <file> PEM text (default: the operand, or stdin)
--label <LABEL> Refuse any block with another label
--index <n> Extract block n (from 0); E_NOT_FOUND past the last block
--encoding pem|der|hex Encoding of the extracted block (default der)
--output, -o <file> Write the block to a file (default stdout); needs --index
--format, -f text|json Format of the listing
pem encode Description
--input, -i <file> DER bytes (default: the operand, or stdin); PEM input is refused (E_INPUT)
--label <LABEL> The block label, e.g. CERTIFICATE (required)
--output, -o <file> Write the PEM text to a file (default stdout)

pkinative oid #

Object identifiers: names, X.690 §8.19–8.20 encodings, validation and the registry pkinative knows.

pkinative oid name <oid>...
pkinative oid encode <oid> [--tlv] [--relative] [--encoding hex|der|pem]
pkinative oid decode <hex> [--tlv] [--relative]
pkinative oid decode --input <file> --tlv
pkinative oid validate <oid>...
pkinative oid list [--filter <text>]
Subcommand Flags
name <oid>... --format, -f text|json. Prints each OID's registered name, or that it is not registered
encode <oid> --tlv (the whole TLV — tag 06, or 0D with --relative — instead of the content octets); --relative (RELATIVE-OID, needs --tlv); --encoding pem|der|hex (default hex); --label <LABEL> (with --encoding pem); --output, -o <file>
decode <hex> --input, -i <file> (read the bytes from a file instead of the operand); --tlv (the bytes are a whole TLV); --relative (needs --tlv); --format, -f
validate <oid>... --format, -f. Exit 1 (E_CHECK_FAILED) when one is invalid
list --filter <text> (keep entries whose OID, name or standard contains it); --format, -f

pkinative fingerprint #

Digests of one PKI object (certificate, request, CRL, public key, …) given as one PEM block or DER object; no subcommand.

pkinative fingerprint [<file>] [--alg SHA-256] [--separator :] [--case upper]
pkinative fingerprint [<file>] --key-id [--alg SHA-1|SHA-256]
pkinative fingerprint [<file>] --shake256 <bytes>
Flag Description
--input, -i <file> The object (default: the operand, or stdin)
--alg SHA-1|SHA-256|SHA-384|SHA-512 Digest (default SHA-256; SHA-1 for --key-id, as RFC 5280 §4.2.1.2 specifies)
--separator <text> Between bytes (default :; "" for none)
--case upper|lower Hex letter case (default upper)
--webcrypto Digest with Web Crypto instead of the pure-TypeScript path (fingerprint mode only)
--key-id The key identifier of the public key of a certificate, request or PUBLIC KEY
--shake256 <bytes> SHAKE256 of the input, this many bytes (at most 1 MiB)
--format, -f text|json Report format

pkinative asn1 #

X.690 DER, or BER with the global --ber. decode prints the node tree like openssl asn1parse (offset, depth, lengths, tag, value), reads one node with a typed reader, or re-encodes it; encode turns a JSON node spec into DER (pkinative schema asn1-spec).

pkinative asn1 decode [<file>] [--path i.j.k] [--read <type>] [--reencode]
pkinative asn1 decode [<file>] --sequence [--allow-trailing]
pkinative asn1 encode --spec <spec.json> [--encoding der|hex|pem]
asn1 decode Description
--input, -i <file> The object, DER or one PEM block (default: the operand, or stdin)
--path <i.j.k> Select a node by child indices from the root; E_NOT_FOUND past a node's children
--read <type> Read the node: boolean, integer, small-integer, enumerated, null, bit-string, octet-string, oid, relative-oid, string, time
--string-type <type> With --read string on an IMPLICIT-tagged node: utf8, numeric, printable, teletex, ia5, visible, universal, bmp
--time-type UTCTime|GeneralizedTime With --read time on an IMPLICIT-tagged node
--reencode Re-encode the node (byte-identical for DER; the status says identical)
--sequence Decode concatenated top-level objects
--allow-trailing Ignore bytes after the first object
--encoding pem|der|hex --reencode output encoding (default der)
--label <LABEL> PEM label with --encoding pem
--output, -o <file> --reencode output file (default stdout)
--format, -f text|json Report format
asn1 encode Description
--spec <file> JSON node spec (- = stdin; or the operand). Types: boolean, integer, enumerated, null, bit-string, named-bits, octet-string, oid, relative-oid, string, time, sequence, set, set-of, explicit, implicit, tlv, der
--encoding pem|der|hex Output encoding (default der)
--label <LABEL> PEM label with --encoding pem
--output, -o <file> Output file (default stdout)

Certificates #

pkinative cert #

RFC 5280 certificates: read, issue, encode their building blocks, verify one signature, and check names and purposes.

pkinative cert inspect [<file>] [--extension <kind>] [--raw-extensions]
pkinative cert create --spec <spec.json> --key <key> [--issuer <ca.pem>] [--public-key <file>] [-o <file>]
pkinative cert encode <structure> --spec <spec.json>
pkinative cert encode signature-algorithm --key <key> [--rsa-scheme <scheme>]
pkinative cert decode-extension --oid <oid> --value <hex> [--critical]
pkinative cert verify-signature [<file>] [--issuer <file>]
pkinative cert check-name [<file>] --host <name> | --ip <address>
pkinative cert match-name <presented> <reference> [--no-wildcards]
pkinative cert check-purpose [<file>] --purpose <name|oid|any> [--chain <file>]...
cert inspect Description
--input, -i <file> The certificate, PEM or DER (default: the operand)
--extension <kind> Print one decoded extension (E_NOT_FOUND if absent). The 19 kinds: basicConstraints, keyUsage, extendedKeyUsage, subjectAltName, issuerAltName, subjectKeyIdentifier, authorityKeyIdentifier, nameConstraints, certificatePolicies, policyMappings, policyConstraints, inhibitAnyPolicy, authorityInfoAccess, subjectInfoAccess, crlDistributionPoints, freshestCRL, signedCertificateTimestampList, ocspNoCheck, subjectDirectoryAttributes
--raw-extensions Keep every extension undecoded
--format, -f text|json Report format
cert create Description
--spec <file> JSON spec (- = stdin): subject, issuer, serialNumber, notBefore, notAfter or validityDays, extensions (pkinative schema cert-spec). Defaults when omitted: a random 127-bit serialNumber, notBefore now, validityDays 365
--issuer <file> Issuer certificate; omitted = self-signed
--public-key <file> Subject key: a certificate, request or PUBLIC KEY (default: derived from an unencrypted --key when self-signed)
signing flags --key, --p12, --key-type, --hash, --rsa-scheme, --salt-length, --password-file, --password-stdin (below)
--output, -o <file> Output file (default stdout)
--encoding pem|der|hex Output encoding (default pem)

The new certificate is verified against --issuer (or itself) before it is written: a key that does not belong to the issuer is refused (E_INPUT). The status envelope carries serialNumber, selfSigned and signatureVerified.

cert encode <structure> Description
<structure> name, name-attribute, validity, spki, algorithm-identifier, attribute, extension, extensions, basic-constraints, key-usage, extended-key-usage, subject-alt-name, subject-key-identifier, authority-key-identifier, signature-algorithm (from the signing key)
--spec <file> JSON value of the structure (- = stdin); the members of each spec: pkinative schema cert-encode-spec
signing flags signature-algorithm only: the key decides the algorithm
--output, -o <file> Output file (default stdout)
--encoding pem|der|hex Output encoding (default hex)
--label <LABEL> PEM label with --encoding pem
cert decode-extension Description
--oid <oid> The extension OID (required)
--value <hex> The extnValue content
--input, -i <file> … or read it from a file, DER
--critical Decode as a critical extension
--format, -f text|json Report format
cert verify-signature Description
--input, -i <file> The certificate (default: the operand)
--issuer <file> The issuer certificate (default: the self-signature)
--allow-algorithm-mismatch Accept a tbsCertificate and outer signature algorithm that differ
--format, -f text|json Report format. Exit 1 (E_VERIFY_FAILED) when the signature does not verify
cert check-name Description
--input, -i <file> The certificate (default: the operand)
--host <name> DNS reference identity
--ip <address> IPv4 or IPv6 reference identity
--chain <file> (repeatable) Issuers, for name constraints
--allow-cn-fallback Accept a commonName when there is no SAN
--no-wildcards Refuse wildcard names
--format, -f text|json Report format. Exit 1 (E_CHECK_FAILED) on a mismatch
cert match-name <presented> <reference> Description
--no-wildcards Refuse wildcard names
--format, -f text|json Report format. Exit 1 (E_CHECK_FAILED) on a mismatch
cert check-purpose Description
--input, -i <file> The certificate (default: the operand)
--purpose <name|oid|any> serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, ocspSigning, any, or an OID
--chain <file> (repeatable) The issuers, leaf-first order
--no-restrict-issuers Do not require the purpose in issuer EKUs
--require-explicit-purpose Refuse a certificate without an EKU
--format, -f text|json Report format. Exit 1 (E_CHECK_FAILED) when the purpose fails

Signing flags (cert create, cert encode signature-algorithm, csr create, cms sign). The algorithm follows the key: ECDSA on the key's curve with the curve's hash, Ed25519 and Ed448 without a hash, RSA only with an explicit scheme.

Flag Description
--key <file> PKCS#8 private key, plain or PBES2-encrypted
--p12 <file> PKCS#12 file holding one key (PBES2 and PBMAC1)
--key-type <type> Type of an encrypted key when no certificate or public key tells it: ec-p256, ec-p384, ec-p521, ed25519, ed448, rsa, rsa-pss
--hash SHA-256|SHA-384|SHA-512 Digest (default: the curve's, or SHA-256; SHA-1 only with --allow-sha1)
--rsa-scheme pkcs1|pss Required for an RSA key: there is no default
--salt-length <n> RSA-PSS salt length (default: the hash length)
--password-file <file> Key or PKCS#12 password: the first line of the file
--password-stdin … or the first line of stdin (or PKINATIVE_PASSWORD; never on the command line)

pkinative csr #

PKCS#10 certification requests (RFC 2986).

pkinative csr inspect [<file>] [--raw-extensions]
pkinative csr create --spec <spec.json> --key <key> [--public-key <file>]
pkinative csr verify [<file>]
Subcommand Flags
inspect --input, -i <file> (the request, PEM or DER; default the operand); --raw-extensions (keep requested extensions undecoded); --format, -f text|json
create --spec <file> ({ "subject": {...}, "extensions": {...} }, pkinative schema csr-spec; - = stdin); --public-key <file> (the key to certify; default derived from an unencrypted --key); the signing flags; --output, -o <file>; --encoding pem|der|hex (default pem). The proof of possession is verified before the request is written
verify --input, -i <file>; --format, -f. Exit 1 (E_VERIFY_FAILED) when the self-signature does not verify

Paths & revocation #

pkinative chain #

RFC 5280 §6 certification paths. verify is the one-call verdict: path building, every signature, RFC 5280 validation, the server name (RFC 6125), the purposes and, with CRLs or OCSP responses, revocation. build finds a path without checking signatures: its verdict is the path, so valid is false with PKI_REASON_SIGNATURE_NOT_CHECKED, the envelope says signaturesChecked: false, and it exits 0 whenever a path is found, 1 when none is. validate takes the path in order and verifies each link's signature first. A negative verdict prints the report, then exits 1 (E_VERIFY_FAILED). Offline: CRLs and OCSP responses are files you fetched.

pkinative chain verify [<leaf>] --trust <roots> [--untrusted <file>]... [--host <name> | --ip <address>]
                       [--purpose <p>]... [--crl <file>]... [--ocsp <file>]... [--require-revocation] [--at <instant>]
pkinative chain build [<leaf>] --trust <roots> [--untrusted <file>]...
pkinative chain validate --path <leaf> --path <ca>... --trust <roots>
Flag verify build validate Description
--input, -i <file> ✓ ✓ The leaf (default: the operand)
--path <file> (repeatable) ✓ The path, leaf first (or operands)
--trust <file> (repeatable) ✓ ✓ ✓ Trust anchors, PEM bundle or DER (required)
--untrusted <file> (repeatable) ✓ ✓ Intermediate candidates
--at <instant> ✓ ✓ ✓ Validation time: ISO 8601 (UTC unless zoned), epoch milliseconds or now (default now)
--purpose <name|oid> (repeatable) ✓ ✓ ✓ Required extended key usage: serverAuth, clientAuth, codeSigning, …
--policy <oid> (repeatable) ✓ ✓ ✓ Initial policy set
--require-explicit-policy, --inhibit-policy-mapping, --inhibit-any-policy ✓ ✓ ✓ RFC 5280 §6.1.1 policy inputs
--host <name> / --ip <address> ✓ The DNS name or IP address the leaf must hold
--crl <file> (repeatable) ✓ CRLs, PEM bundle or DER
--ocsp <file> (repeatable) ✓ OCSP responses, DER or PEM (a PEM bundle is accepted)
--ocsp-nonce <hex> ✓ The nonce the OCSP request carried (1–32 octets)
--require-ocsp-nonce ✓ Refuse an OCSP response without that nonce
--require-revocation ✓ Fail when a certificate's status is unknown
--no-signatures ✓ Skip the link signatures: a structural verdict, headed as such
--format, -f text|json ✓ ✓ ✓ Report format

The chain envelopes carry valid, pathLength and signaturesChecked; verify --summary prints valid, the reason codes, the path's subjects, explored and signatureVerifications.

pkinative crl #

RFC 5280 §5 certificate revocation lists. find reports whether a serial is listed and exits 0 either way; check is the verdict — listed, stale, wrong scope or unverified is exit 1 (E_CHECK_FAILED). With --issuer the CRL signature is verified; without it, it counts as unchecked.

pkinative crl inspect [<crl>]
pkinative crl find [<crl>] --serial <hex> | --cert <file>
pkinative crl verify-signature [<crl>] --issuer <ca>
pkinative crl check [<crl>] --cert <file> [--issuer <ca>] [--delta <crl>] [--at <instant>] [--stale-tolerance <ms>]
Subcommand Flags
inspect --input, -i <file> (the CRL, PEM or DER; default the operand); --format, -f text|json
find --input, -i; --serial <hex> (the serial number, hexadecimal) or --cert <file> (the certificate to look up); --format, -f
verify-signature --input, -i; --issuer <file> (the CRL issuer, required); --format, -f. Exit 1 (E_VERIFY_FAILED) when it does not verify
check --input, -i; --cert <file> (the certificate to check, required); --issuer <file>; --delta <file> (a delta CRL to apply on top; one without a deltaCRLIndicator is E_INPUT); --at <instant> (default now); --stale-tolerance <ms> (accept a CRL this long past nextUpdate); --format, -f

pkinative ocsp #

RFC 6960, offline: the CLI writes the request and judges the response; sending the request is yours (curl --data-binary @req.der -H 'Content-Type: application/ocsp-request').

pkinative ocsp request --cert <file> --issuer <ca> [--hash SHA-1|SHA-256] [--nonce <hex>|random] [-o req.der]
pkinative ocsp cert-id --cert <file> --issuer <ca> [--hash SHA-1|SHA-256]
pkinative ocsp inspect [<response>]
pkinative ocsp verify-signature [<response>] [--responder <cert>]
pkinative ocsp check [<response>] --cert <file> --issuer <ca> [--responder <cert>] [--nonce <hex>] [--at <instant>]
Subcommand Flags
request --cert <file> (or the operand); --issuer <file> (required); --hash SHA-1|SHA-256 (CertID hash, default SHA-1, as responders expect); --nonce <hex>|random (an RFC 8954 nonce of 1 to 32 octets, printed in the status); --output, -o <file>; --encoding pem|der|hex (default der); --label <LABEL>
cert-id --cert <file> (or the operand); --issuer <file>; --hash SHA-1|SHA-256; --output, -o; --encoding (default hex); --label
inspect --input, -i <file> (the response, DER or PEM; default the operand); --format, -f text|json
verify-signature --input, -i; --responder <file> (default: the certificates the response embeds); --format, -f. Exit 1 (E_VERIFY_FAILED) when no responder certificate verifies it
check --input, -i; --cert <file>, --issuer <file> (both required); --responder <file> (default: the embedded certificates, then the issuer); --responder-trusted (trust the responder out of band; otherwise it must be the CA or a delegate the CA issued with OCSPSigning, RFC 6960 §4.2.2.2); --hash SHA-1|SHA-256 (default: the response's own); --nonce <hex>; --require-nonce; --at <instant>; --stale-tolerance <ms>; --future-tolerance <ms> (accept a thisUpdate this far ahead, default 60000); --format, -f

ocsp check exits 1 (E_CHECK_FAILED) unless the answer is a clean, signed, authorised good for this certificate.

Signatures & time-stamps #

pkinative cms #

CMS SignedData (RFC 5652, with RFC 5035 signing-certificate-v2 and RFC 6211 algorithm protection).

pkinative cms sign --content <file> --cert <cert> --key <key> [--detached] [--chain <file>]... [-o out.p7s]
pkinative cms verify [<p7s>] [--content <file>] --trust <roots>
pkinative cms inspect [<p7s>]
pkinative cms verify-signer [<p7s>] --cert <cert> [--signer-index <n>]
pkinative cms add-attribute [<p7s>] --attribute <der> [--signer-index <n>]
pkinative cms add-timestamp [<p7s>] --token <tst|tsr> [--signer-index <n>]
cms sign Description
--content <file> The content to sign
--content-digest <hex> Sign a precomputed digest instead (needs --detached)
--cert <file> The signer certificate (or the one in --p12)
--chain <file> (repeatable) Certificates to embed
--crl <file> (repeatable) CRLs to embed
--detached Leave the content out of the SignedData
--content-type <oid> eContentType (default id-data)
--sid issuer-serial|ski Signer identifier form (default issuer-serial)
--signing-time <instant|now|none> signingTime attribute (default now)
--no-signing-certificate Omit ESS signing-certificate-v2
--no-algorithm-protection Omit CMS algorithm protection
--signed-attribute <file> (repeatable) Extra signed attribute, DER
--unsigned-attribute <file> (repeatable) Extra unsigned attribute, DER
signing flags --key, --p12, --key-type, --hash, --rsa-scheme, --salt-length, --password-file, --password-stdin
--output, -o <file> Output file (default stdout)
--encoding pem|der|hex Output encoding (default der; PEM label CMS)

The new signature is verified under --cert before it is written; a key that does not belong to the certificate is E_INPUT.

cms verify Description
--input, -i <file> The SignedData, PEM or DER (default: the operand)
--content <file> / --content-digest <hex> The content of a detached signature, or its digest
--trust <file> (repeatable) Trust anchors (required)
--untrusted <file> (repeatable) Extra certificates for the chains
--purpose <name|oid> (repeatable) Required extended key usage
--crl <file>, --ocsp <file> (repeatable), --require-revocation Revocation evidence
--at <instant> Validation time (default now)
--at-timestamp Validate at the signer's verified timestamp
--require-signing-certificate, --require-algorithm-protection Demand those signed attributes
--allow-trailing Accept bytes after the SignedData
--format, -f text|json Report format. Exit 1 (E_VERIFY_FAILED) unless every signer verifies
Subcommand Flags
inspect --input, -i <file>; --allow-trailing; --format, -f text|json
verify-signer --input, -i; --cert <file> (the signer certificate, required); --signer-index <n> (default 0); --content <file> (of a detached signature); --format, -f. A key check on one signer, not the verdict
add-attribute --input, -i; --signer-index <n>; --attribute <file> (one Attribute, DER, required); --output, -o; --encoding (default der)
add-timestamp --input, -i; --signer-index <n>; --token <file> (a TimeStampToken or TimeStampResp, required); --output, -o; --encoding (default der)

A counter timestamp stamps the signer's signature value, not the content: take it from cms inspect --fields signerInfos.signature, hash it, and request the timestamp over that digest (tsp request --digest). add-timestamp refuses a token over anything else before writing a byte (E_INPUT, PKI_REASON_TSP_IMPRINT_MISMATCH in error.reasons, the recipe as remedy).

pkinative tsp #

RFC 3161 timestamping, offline: the CLI writes the request and judges the answer; posting it to a TSA is yours (curl --data-binary @req.tsq -H 'Content-Type: application/timestamp-query').

pkinative tsp request --data <file> | --digest <hex> [--hash SHA-256] [--nonce <n>|random] [--policy <oid>] [-o req.tsq]
pkinative tsp inspect [<file>] [--as response|token|tstinfo]
pkinative tsp verify --response <tsr> | --token <tst> --trust <roots> (--request <tsq> | --data <file> | --digest <hex>)
Subcommand Flags
request --data <file> (hashed with --hash) or --digest <hex>; --hash SHA-256|SHA-384|SHA-512 (imprint hash, default SHA-256); --nonce <n>|random (decimal, 0x-hex, or 63 random bits; printed in the status); --policy <oid>; --no-cert-req (do not ask the TSA to embed its certificate); --output, -o <file>; --encoding pem|der|hex (default der); --label <LABEL>
inspect --input, -i <file> (default the operand); --as response|token|tstinfo (skip the detection); --format, -f text|json
verify --response <file> (or the operand) or --token <file>; --request <file> (nonce and imprint must match it), --data <file> or --digest <hex>; --trust <file> (repeatable, required); --untrusted <file> (repeatable); --crl <file>, --ocsp <file> (repeatable), --require-revocation; --at <instant>; --allow-noncritical-eku (accept a TSA whose timeStamping EKU is not critical); --format, -f. Exit 1 (E_VERIFY_FAILED) unless the timestamp verifies

Keys #

pkinative key #

PKCS#8 private keys (RFC 5958, PBES2 per RFC 8018). inspect describes a key — algorithm, curve, attributes, or the PBES2 parameters of an encrypted key — without a password; check imports or decrypts it into a non-extractable Web Crypto key to prove it is usable. No report ever contains a key byte.

pkinative key inspect [<file>]
pkinative key check [<file>] [--key-type <type>] [--rsa-scheme pkcs1|pss]
Subcommand Flags
inspect --input, -i <file> (the key, PEM or DER; default the operand); --format, -f text|json
check --input, -i; --key-type <type> (of an encrypted key: ec-p256, ec-p384, ec-p521, ed25519, ed448, rsa, rsa-pss); --hash SHA-256|SHA-384|SHA-512 (the digest the key will sign with); --rsa-scheme pkcs1|pss (required for an RSA key); --salt-length <n>; --password-file <file>; --password-stdin; --format, -f

pkinative p12 #

PKCS#12 (RFC 7292) under PBES2 and PBMAC1 (RFC 9579) only. inspect needs no password; the others read it from --password-file, --password-stdin or PKINATIVE_PASSWORD. The legacy RC2 and 3DES ciphers are refused by doctrine (E_SECURITY, with the two-step OpenSSL conversion as remedy). A MAC keyed with the RFC 7292 Appendix B KDF is never checked: verify-mac refuses it (E_SECURITY, PKI_KEY_MAC_UNSUPPORTED), and open reports it as unverified integrity (E_VERIFY_FAILED) unless --allow-unverified-integrity is given. No report ever contains a key byte.

pkinative p12 inspect [<file>]
pkinative p12 verify-mac [<file>]
pkinative p12 bags [<file>]
pkinative p12 open [<file>] [--certs-out <bundle.pem>] [--rsa-scheme pkcs1|pss]
Subcommand Flags
inspect --input, -i <file> (default the operand); --format, -f text|json
verify-mac --input, -i; --password-file <file>; --password-stdin; --format, -f. Exit 1: E_PASSWORD when the MAC does not match (a wrong password or an altered file), E_VERIFY_FAILED when the file carries no MAC
bags --input, -i; --password-file; --password-stdin; --format, -f. Lists every bag, decrypting the encrypted contents
open --input, -i; --password-file; --password-stdin; --allow-unverified-integrity (accept a file whose MAC cannot be checked); --rsa-scheme pkcs1|pss (import an RSA key for this scheme); --hash SHA-256|SHA-384|SHA-512 (the RSA key's digest, default SHA-256); --certs-out <file> (write the certificates and CRLs as PEM, only from a file that opened); --format, -f. Exit 0 only when everything opened; otherwise exit 1 — E_PASSWORD on a MAC mismatch, E_VERIFY_FAILED when the MAC cannot be checked, E_SECURITY for a legacy cipher — except an RSA key without --rsa-scheme: E_USAGE, exit 2

Meta #

Command Flags Does
pkinative doctor --format, -f text|json Offline preflight: the Node.js security floor, the installed pkinative against the range the CLI was built for, and whether Web Crypto can verify, sign and decrypt. Exit 1 (E_CHECK_FAILED) when a check fails
pkinative limits --format, -f text|json (and any --max-*) The 22 pkinative bounds with the flag that raises each, its default, the effective value under the --max-* flags given and its CWE, plus the CLI's --max-content-size; --format json adds what each guards
pkinative explain [<code>] --list; --kind error|reason|diagnostic|cli (with --list); --format, -f text|json Explains a code; E_NOT_FOUND for an unknown one
pkinative schema [<subject>] — (up to three operands) The schema subjects
pkinative completion <shell> — A completion script for bash, zsh, fish or powershell

Global options #

Flag Description
--help, -h Global or per-command help, on stdout
--version, -V The CLI version; with --json, { name, version, pkinative }
--json Agent mode: compact JSON report on stdout, one JSON envelope on stderr (also PKINATIVE_JSON=1)
--pretty Indent JSON output
--fields <a,b.c> Keep only these dot-paths of a JSON report
--summary Print the command's minimal JSON shape
--quiet, -q No notes or diagnostics on stderr; errors and envelopes still print (also PKINATIVE_QUIET=1)
--no-color No ANSI colour; colour is also off under NO_COLOR, TERM=dumb or a pipe, and forced by FORCE_COLOR
--dry-run Validate everything, write nothing (the status says dryRun: true)
--strict Escalate the first engine warning to E_CHECK_FAILED
--ber Accept BER as well as DER (common in CMS)
--pem-mode strict|lax PEM reading: RFC 7468 strict (default) or lax
--allow-sha1 Accept or produce SHA-1 signatures (legacy material you trust)
--overwrite Replace an existing output file, atomically
--config <file> Use this .pkinativerc.json (default: the nearest one upward)
--no-config Ignore any .pkinativerc.json
--max-content-size <size> The CLI's own bound on content read whole — content to sign, verify or timestamp, and JSON specs (default 1 GiB)
--max-<limit> <n> The 22 pkinative limits (below)

The 22 --max-* flags are the keys of pkinative's DEFAULT_PKI_LIMITS in kebab case: --max-input-bytes (64 MiB, read before a byte is buffered), --max-depth, --max-nodes, --max-integer-bytes, --max-oid-bytes, --max-ber-segments, --max-pem-blocks, --max-extensions, --max-general-names, --max-name-attributes, --max-policies, --max-chain-length, --max-policy-nodes, --max-revoked-certificates, --max-ocsp-single-responses, --max-paths-explored, --max-signer-infos, --max-attributes, --max-cms-certificates-and-crls, --max-kdf-iterations, --max-pkcs12-bags, --max-pkcs12-kdf-iterations. A byte bound accepts a binary suffix (65536, 64k, 16MiB, 1g); a count bound takes digits only. Every value is a positive integer: pkinative refuses an unbounded limit, and so does the CLI. pkinative limits prints each bound with its default and CWE; the security guide explains what each one guards.

Configuration file #

.pkinativerc.json is found by walking upward from the working directory, the nearest file winning, unless --config <file> names one or --no-config disables the mechanism. It is presentation only: a file planted in a cloned repository must never change what a command reads, trusts, when it judges, or where it writes. So it may set nine keys and nothing else — json, pretty, quiet, no-color, format, encoding, fields, summary, strict (which only tightens) — and any other key, present or future, is E_USAGE: an input, a trust anchor, the validation time, a tolerance, a bound, an output path or a password source can be set on the command line only.

A top-level key applies to every command; a key naming a command ("cert") or a subcommand ("cert inspect") holds a section, and the most specific section wins. A key applies only where the invoked subcommand declares the flag, and an explicit flag always beats the file. Switches take true or false, the three value keys take a string; a wrong type, a section naming no subcommand, __proto__, constructor, prototype, invalid JSON, a non-object or a file above 1 MiB is E_USAGE. The file that supplied defaults is named in the envelope (config) and in a note on stderr.

{
  "no-color": true,
  "cert inspect": { "format": "json", "fields": "subject,validity" },
  "cms sign": { "encoding": "pem" }
}

pkinative schema config prints the file's JSON Schema.

Environment #

Variable Effect
PKINATIVE_JSON=1 Same as --json
PKINATIVE_QUIET=1 Same as --quiet
PKINATIVE_PASSWORD The third password source, beside --password-file and --password-stdin; giving two sources is E_USAGE
PKINATIVE_DEBUG=1 Adds the stack trace of a failure to stderr
NO_COLOR / FORCE_COLOR / TERM=dumb Colour of the text report, which is otherwise on when stdout is a terminal (--no-color also turns it off)

The recommended loop for an agent: run pkinative doctor --json once per environment; run the command with --json (and --summary when only the verdict matters); on exit 1 read error.code, then error.pkiCode and error.reasons, and apply error.remedy only when the input is trusted and the policy allows it; on exit 2 fix the invocation and never retry it unchanged.

Security posture #

The CLI repository's SECURITY.md and threat model hold the full posture; the security guide covers the engine underneath.

Shell completion #

Completion scripts are generated from the command registry: commands and subcommands after global flags, flags, the values of enumerated flags under every spelling, paths after a file flag, and the shell names after completion.

pkinative completion bash > /etc/bash_completion.d/pkinative
pkinative completion zsh  > "${fpath[1]}/_pkinative"
pkinative completion fish > ~/.config/fish/completions/pkinative.fish
pkinative completion powershell >> $PROFILE

Conformance #

The CLI computes no verdict of its own, so the engine's conformance — x509-limbo, NIST PKITS, the pinned RFC texts and the OpenSSL and Windows interop matrix — is the CLI's; the conformance guide carries the figures. What the CLI repository adds is proof that nothing is lost on the way through the process boundary:

Not supported #

Not supported Instead
Network access None, in any mode: you fetch CRLs, OCSP responses, timestamps and intermediates
Key generation, key export Bring keys from an HSM, a KMS or openssl genpkey
Writing a PKCS#8 or PKCS#12 Use OpenSSL; the CLI opens and verifies them
Legacy PKCS#12 ciphers (RC2, 3DES) and PBES1 keys; checking an RFC 7292 Appendix B MAC Refused by doctrine; the remedy gives the one-time OpenSSL conversion (a file with an Appendix B MAC opens, unverified, under --allow-unverified-integrity)
An RSA signature without a named scheme --rsa-scheme pkcs1|pss is required
SHA-1 signatures by default --allow-sha1, for legacy material you already trust
A password on the command line --password-file, --password-stdin or PKINATIVE_PASSWORD
An unbounded limit Every --max-* value is a positive integer
Raising a bound or naming a trust anchor from a config file Command line only

The engine's own refusals — DSA verification, ETSI long-term signature formats, names compared by §7.1 preparation — are listed in the choosing guide.

Versioning #

pkinative-cli follows Semantic Versioning, and its agent contract is stable across 1.x: the commands and their flags, the exit codes, the 13 E_* classes and the PKI_* → E_* mapping, the envelope fields, the schema subjects and the configuration keys; fields are only ever added. pkinative-cli 1.0.0, released on 2026-10-08, pins pkinative ^1.0.0; security fixes land on the latest 1.x minor, for as long as the engine's 1.x line is supported.

Further reading #