PDF Signature Verification in Node.js: Headless Server-Side Processing
While browser-side verification is ideal for interactive user experiences, many workflows require server-side PDF signature verification — automated document processing, CI/CD pipeline validation, compliance auditing, and batch processing of signed documents. VerifyKit's core engine runs headlessly in Node.js 20+, Deno, and Bun with zero DOM dependencies.
Installing the Core Package
npm install @trexolab/verifykit-coreNo native add-ons, no node-gyp, no platform-specific binaries. The package contains a pure WebAssembly module that runs on any platform supporting Node.js 20+.
Basic Verification
import { createVerifier } from '@trexolab/verifykit-core'
import { readFileSync } from 'fs'
const verifier = await createVerifier()
const pdfBuffer = readFileSync('contract.pdf')
const result = await verifier.verify(pdfBuffer)
console.log(`Signatures found: ${result.signatures.length}`)
for (const sig of result.signatures) {
console.log(`\n${sig.signerName}`)
console.log(` Overall: ${sig.overallStatus}`)
console.log(` Integrity: ${sig.integrityCheck.status}`)
console.log(` Signature: ${sig.signatureCheck.status}`)
console.log(` Chain: ${sig.certificateChainCheck.status}`)
console.log(` Expiry: ${sig.expiryCheck.status}`)
console.log(` Timestamp: ${sig.timestampCheck.status}`)
console.log(` Revocation: ${sig.revocationCheck.status}`)
console.log(` Algorithm: ${sig.algorithmCheck.status}`)
console.log(` EKU: ${sig.ekuCheck.status}`)
console.log(` PAdES: ${sig.padesLevel ?? 'N/A'}`)
}Batch Processing
Process a directory of signed PDFs:
import { createVerifier } from '@trexolab/verifykit-core'
import { readdir, readFile } from 'fs/promises'
import { join } from 'path'
const verifier = await createVerifier()
const dir = './signed-documents'
const files = (await readdir(dir)).filter(f => f.endsWith('.pdf'))
const results = await Promise.all(
files.map(async (file) => {
const pdf = await readFile(join(dir, file))
const result = await verifier.verify(pdf)
return {
file,
signatures: result.signatures.length,
valid: result.signatures.every(s => s.overallStatus === 'valid'),
statuses: result.signatures.map(s => ({
signer: s.signerName,
status: s.overallStatus,
pades: s.padesLevel,
})),
}
})
)
// Summary
const valid = results.filter(r => r.valid).length
console.log(`\n${valid}/${results.length} documents fully valid`)Online Revocation Checking
For server-side verification, you can perform direct CRL/OCSP requests without a proxy (since there are no CORS restrictions in Node.js):
import { createVerifier } from '@trexolab/verifykit-core'
import { createRevocationPlugin } from '@trexolab/verifykit-plugin-revocation'
const revocation = createRevocationPlugin({ mode: 'direct' })
const verifier = await createVerifier({
plugins: [revocation],
})
const result = await verifier.verify(pdfBuffer)
// Revocation checks now include live CRL/OCSP responsesCI/CD Pipeline Integration
Add signature verification to your CI/CD pipeline:
import { createVerifier } from '@trexolab/verifykit-core'
import { readFileSync } from 'fs'
async function validateDocument(path: string): Promise<boolean> {
const verifier = await createVerifier()
const result = await verifier.verify(readFileSync(path))
if (result.signatures.length === 0) {
console.error(`FAIL: ${path} — no signatures found`)
return false
}
for (const sig of result.signatures) {
if (sig.overallStatus === 'invalid') {
console.error(
`FAIL: ${path} — ${sig.signerName}: ${sig.overallStatus}`
)
console.error(` Integrity: ${sig.integrityCheck.detail}`)
return false
}
}
console.log(`PASS: ${path} — ${result.signatures.length} valid signature(s)`)
return true
}
// Exit with error code if validation fails
const path = process.argv[2]
if (!path) {
console.error('Usage: node validate.mjs <path-to-pdf>')
process.exit(1)
}
const ok = await validateDocument(path)
process.exit(ok ? 0 : 1)Custom Trust Store
For enterprise environments with internal CAs:
const verifier = await createVerifier({
trustStore: {
certificates: [
readFileSync('corp-root-ca.pem', 'utf-8'),
readFileSync('dept-intermediate-ca.pem', 'utf-8'),
],
mode: 'merge', // keeps the 119 AATL roots + adds yours
},
})Use mode: 'replace' if you only want to trust your own CAs and discard the built-in AATL roots.
Certification and Permissions
Server-side verification can also check document certification (DocMDP) permissions:
for (const sig of result.signatures) {
if (sig.mdpPermission != null) {
console.log(`Certified (P=${sig.mdpPermission})`)
// P=1: No changes allowed
// P=2: Form fill + sign only
// P=3: Annotate + form fill + sign
} else {
console.log('Approval signature')
}
}Runtime Support
The same core package works across JavaScript runtimes:
| Runtime | Minimum Version |
|---|---|
| Node.js | 20.19.0+ |
| Deno | Latest |
| Bun | Latest |
No configuration differences between runtimes. The WASM binary is base64-embedded, so there are no file system dependencies to manage.
Next Steps
- Core API Reference — Full type and function reference
- Revocation Checking — CRL/OCSP configuration
- Testing Guide — Mock verification for unit tests
- Contact Us — Need help with server-side integration?
Ready to verify PDF signatures in your application?
Get started with VerifyKit in under 5 minutes.