VERIPDFSDK

Getting Started with VeriPDF SDK

VeriPDF is a modular PDF signature verification toolkit powered by a Rust/WASM core engine. It ships as a set of npm packages that can be used independently or together. The Rust/WASM core provides built-in cryptographic verification with no external crypto dependencies, running 3.7x faster than the previous JavaScript engine.


Prerequisites

RequirementMinimumNotes
Node.js20.19.0+Required for development, build tools, and @trexolab/verifykit-core in Node.js
BrowserChrome 109+, Firefox 115+, Safari 16.4+, Edge 109+Must support ES2022+, structuredClone, WebAssembly
React (if using @trexolab/verifykit-react)19+Not needed for @trexolab/verifykit-vanilla or @trexolab/verifykit-core

Note on WebAssembly: The @trexolab/verifykit-core package loads a .wasm binary at runtime. Your bundler and hosting environment must support serving .wasm files. Most modern bundlers (Vite, webpack 5, Turbopack) handle this automatically.

Note on pdfjs-dist: The pdfjs-dist version is pinned to 5.5.207. Do not upgrade it independently -- newer versions may introduce breaking API changes or require additional polyfills.


Packages

PackageDescriptionRuntime
@trexolab/verifykit-coreRust/WASM signature verification engineBrowser + Node.js
@trexolab/verifykit-reactReact PDF viewer with plugin-based architectureBrowser (React 19+)
@trexolab/verifykit-vanillaZero-build CDN/UMD drop-in viewerBrowser
@trexolab/verifykit-plugin-revocationOnline CRL/OCSP revocation checkingBrowser + Node.js

Registry Configuration

VeriPDF packages are published to our internal npm registry. Before installing, configure npm to use the TrexoLab registry for the @trexolab scope:

Create a .npmrc file in your project root:

@trexolab:registry=https://verifykit.trexolab.com/api/registry

This is the recommended approach -- it scopes the registry to @trexolab packages only, and any developer who clones the project will use the correct registry automatically.

Option 2: Global npm configuration

npm config set @trexolab:registry https://verifykit.trexolab.com/api/registry

Option 3: Inline flag

npm install @trexolab/verifykit-react --registry=https://verifykit.trexolab.com/api/registry

Registry URL: https://verifykit.trexolab.com/api/registry

This registry serves metadata and tarballs for all @trexolab/verifykit-* packages. All other packages (React, lucide-react, etc.) are resolved from the default npm registry as usual.


Installation

React Application

npm install @trexolab/verifykit-react

@trexolab/verifykit-core is installed automatically as a dependency.

Headless / Node.js

npm install @trexolab/verifykit-core

With Revocation Checking

npm install @trexolab/verifykit-core @trexolab/verifykit-plugin-revocation

Vanilla JS / CDN (no npm)

Add to your HTML <head>:

<link rel="stylesheet" href="https://unpkg.com/@trexolab/verifykit-vanilla/dist/veripdf.css">
<script src="https://unpkg.com/@trexolab/verifykit-vanilla/dist/veripdf.umd.js"></script>

Or install via npm for ES module usage:

npm install @trexolab/verifykit-vanilla

Full Installation Example

Here is a complete example of setting up a new project with VeriPDF:

# 1. Create your project
mkdir my-pdf-app && cd my-pdf-app
npm init -y
 
# 2. Configure the registry (one-time setup)
echo '@trexolab:registry=https://verifykit.trexolab.com/api/registry' > .npmrc
 
# 3. Install VeriPDF (the React package includes core automatically)
npm install @trexolab/verifykit-react
 
# 4. Optional: add revocation checking
npm install @trexolab/verifykit-plugin-revocation

React Setup

Step 1: Install

npm install @trexolab/verifykit-react

Step 2: Import Styles

Import the CSS file once in your application entry point:

import '@trexolab/verifykit-react/styles.css'

Step 3: Add the Provider

Wrap your application (or the section that uses VeriPDF) with VeriPdfProvider:

import { VeriPdfProvider } from '@trexolab/verifykit-react'
 
function App() {
  return (
    <VeriPdfProvider config={{ theme: { mode: 'system' } }}>
      <PdfApp />
    </VeriPdfProvider>
  )
}

Step 4: Create the Viewer

Use useVerification() for state management and Viewer with plugins for the PDF display:

import { useState, useCallback, useRef } from 'react'
import {
  Viewer,
  WelcomeScreen,
  useVerification,
  defaultLayoutPlugin,
  type ViewerHandle,
} from '@trexolab/verifykit-react'
 
function PdfApp() {
  const verification = useVerification()
  const viewerRef = useRef<ViewerHandle>(null)
  const [layout] = useState(() => defaultLayoutPlugin())
 
  const handleFile = useCallback(
    async (file: File) => {
      try {
        await verification.load(file)
      } catch (err) {
        console.error('Failed to load PDF:', err)
      }
    },
    [verification],
  )
 
  if (!verification.fileBuffer) {
    return (
      <WelcomeScreen
        onOpenFile={handleFile}
        title="My PDF Viewer"
        subtitle="Drag and drop a PDF here, or click to browse"
      />
    )
  }
 
  return (
    <Viewer
      ref={viewerRef}
      fileBuffer={verification.fileBuffer}
      fileName={verification.fileName}
      plugins={[layout.plugin]}
      onOpenFile={handleFile}
      initialState={{
        signatures: verification.signatures,
        unsignedFields: verification.unsignedFields,
        verificationStatus: verification.status ?? undefined,
      }}
    />
  )
}

Step 5: Add Global CSS Reset

Ensure the viewer fills the viewport:

html, body, #root {
  height: 100%;
  margin: 0;
  padding: 0;
}

Headless Verification (Node.js)

For server-side or CLI signature verification with no UI:

import { createVerifier } from '@trexolab/verifykit-core'
import { readFile } from 'node:fs/promises'
 
async function main() {
  // createVerifier() is async -- it initializes the WASM module
  const verifier = await createVerifier()
 
  try {
    const buffer = await readFile('signed-contract.pdf')
    const result = await verifier.verify(buffer, 'signed-contract.pdf')
 
    console.log(`Signatures: ${result.signatures.length}`)
    for (const sig of result.signatures) {
      console.log(`  ${sig.name}: ${sig.overallStatus}`)
    }
  } finally {
    verifier.dispose()
  }
}
 
main()

For one-shot verification without creating a verifier instance:

import { verifyPdf } from '@trexolab/verifykit-core'
import { readFile } from 'node:fs/promises'
 
const buffer = await readFile('document.pdf')
const result = await verifyPdf(buffer, 'document.pdf')
console.log(result.signatures.length, 'signatures found')

Vanilla JS / CDN

Basic HTML Setup

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://unpkg.com/@trexolab/verifykit-vanilla/dist/veripdf.css">
  <style>
    html, body { height: 100%; margin: 0; padding: 0; }
  </style>
</head>
<body>
  <div id="viewer" style="width: 100%; height: 100vh;"></div>
 
  <script src="https://unpkg.com/@trexolab/verifykit-vanilla/dist/veripdf.umd.js"></script>
  <script>
    var viewer = VeriPdf.create(document.getElementById('viewer'), {
      theme: { mode: 'system' },
      features: {
        signatures: true,
        print: true,
        download: true,
        search: true,
        fullscreen: true,
      },
      onError: function (errMsg) {
        console.error('VeriPdf error:', errMsg)
      },
    })
 
    fetch('/document.pdf')
      .then(function (r) { return r.arrayBuffer() })
      .then(function (buf) { return viewer.load(buf, 'document.pdf') })
      .then(function (result) {
        console.log('Signatures:', result.signatures.length)
      })
      .catch(function (err) {
        console.error('Failed to load PDF:', err)
      })
  </script>
</body>
</html>

ESM Import

import { create } from '@trexolab/verifykit-vanilla'
import '@trexolab/verifykit-vanilla/veripdf.css'
 
const viewer = create(document.getElementById('viewer')!, {
  theme: { mode: 'dark' },
  features: { signatures: true, print: true },
})
 
const result = await viewer.load(pdfArrayBuffer, 'report.pdf')
console.log('Signatures:', result.signatures.length)
 
// Always call destroy() when removing the viewer (SPA navigation, etc.)
viewer.destroy()

Next.js Integration

VeriPDF uses browser-only APIs (DOM, WebAssembly, Web Workers). In Next.js, you must ensure VeriPDF components only render on the client.

Step 1: Create a Client Component

Create a wrapper file with the 'use client' directive:

// components/PdfViewer.tsx
'use client'
 
import { useState, useCallback, useRef } from 'react'
import {
  VeriPdfProvider,
  Viewer,
  WelcomeScreen,
  useVerification,
  defaultLayoutPlugin,
  type ViewerHandle,
} from '@trexolab/verifykit-react'
import '@trexolab/verifykit-react/styles.css'
 
export function PdfViewerApp() {
  return (
    <VeriPdfProvider config={{ theme: { mode: 'system' } }}>
      <PdfViewerInner />
    </VeriPdfProvider>
  )
}
 
function PdfViewerInner() {
  const verification = useVerification()
  const viewerRef = useRef<ViewerHandle>(null)
  const [layout] = useState(() => defaultLayoutPlugin())
 
  const handleFile = useCallback(
    async (file: File) => {
      await verification.load(file)
    },
    [verification],
  )
 
  if (!verification.fileBuffer) {
    return <WelcomeScreen onOpenFile={handleFile} />
  }
 
  return (
    <Viewer
      ref={viewerRef}
      fileBuffer={verification.fileBuffer}
      fileName={verification.fileName}
      plugins={[layout.plugin]}
      onOpenFile={handleFile}
      initialState={{
        signatures: verification.signatures,
        verificationStatus: verification.status ?? undefined,
      }}
    />
  )
}

Step 2: Dynamic Import in Pages

Use next/dynamic with ssr: false to prevent server-side rendering:

// app/pdf/page.tsx
import dynamic from 'next/dynamic'
 
const PdfViewerApp = dynamic(
  () => import('@/components/PdfViewer').then(m => m.PdfViewerApp),
  { ssr: false }
)
 
export default function PdfPage() {
  return (
    <div style={{ height: '100vh' }}>
      <PdfViewerApp />
    </div>
  )
}

Step 3: Configure next.config.js

The WASM file must be accessible. If using webpack:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack: (config) => {
    config.experiments = { ...config.experiments, asyncWebAssembly: true }
    return config
  },
}
 
module.exports = nextConfig

Trust Store Configuration

The @trexolab/verifykit-core engine ships with 119 embedded DER-encoded root CA certificates from the Adobe Approved Trust List (AATL). You can extend or replace this store.

Add Enterprise Root CAs

import { createVerifier } from '@trexolab/verifykit-core'
import { readFile } from 'node:fs/promises'
 
const enterpriseRootPem = await readFile('enterprise-root-ca.pem', 'utf-8')
 
// 'merge' adds your cert alongside the built-in AATL store
const verifier = await createVerifier({
  trustStore: {
    certificates: [enterpriseRootPem],
    mode: 'merge',
  },
})

Replace the Built-in Store

// Only your certificates are trusted -- built-in AATL store is ignored
const verifier = await createVerifier({
  trustStore: {
    certificates: [myRootPem],
    mode: 'replace',
  },
})

Trust Store in React / Vanilla

// React
<VeriPdfProvider config={{ trustStore: { certificates: [pem], mode: 'merge' } }}>
 
// Vanilla
const viewer = VeriPdf.create(container, {
  trustStore: { certificates: [pem], mode: 'merge' },
})

Troubleshooting

1. "WASM module not initialized" Error

Symptom: @trexolab/verifykit-core-wasm: WASM module not initialized. Call await initWasm() or await createVerifier() first.

Cause: You are calling a synchronous function (like buf2hex) before the WASM module has loaded.

Fix: Ensure you call await createVerifier() or await initWasm() before using any core functions. When using @trexolab/verifykit-react, the VeriPdfProvider handles initialization automatically.


2. createVerifier() is Not a Constructor / Returns Promise

Symptom: TypeError: createVerifier is not a constructor or the returned object has no .verify() method.

Cause: In v0.3.0+, createVerifier() is async and returns a Promise<VeriPdfVerifier>. Older code that calls it synchronously will break.

Fix:

// Old (v0.2.x) -- no longer works:
const verifier = createVerifier({ ... })
 
// New (v0.3.0+) -- must await:
const verifier = await createVerifier({ ... })

3. WASM File Not Found (404)

Symptom: Network error loading .wasm file, or CompileError: WebAssembly.instantiate().

Cause: The bundler is not serving the .wasm binary alongside the JavaScript output.

Fix: Ensure your bundler supports WASM. For Vite, this works out of the box. For webpack, enable asyncWebAssembly:

// webpack.config.js
module.exports = {
  experiments: { asyncWebAssembly: true },
}

For Next.js, see the Next.js Integration section.


4. "Cannot use import statement outside a module" in Node.js

Symptom: SyntaxError when running @trexolab/verifykit-core in Node.js.

Cause: The package uses ES module syntax, but your Node.js project is configured for CommonJS.

Fix: Either:

  • Add "type": "module" to your package.json, or
  • Use the CJS entry point by ensuring your bundler resolves require('@trexolab/verifykit-core'), or
  • Rename your file to .mjs

5. PDF.js Worker Fails to Load

Symptom: Console error about worker initialization, blank viewer, or pdf.worker.min.mjs 404.

Cause: The PDF.js worker script cannot be loaded from the CDN, or Content Security Policy blocks blob: URLs.

Fix:

  • Ensure your CSP allows worker-src 'self' blob:; script-src 'self' blob:;
  • If the CDN is blocked, self-host the worker:
    cp node_modules/@trexolab/verifykit-react/dist/pdf.worker.min.mjs public/
    Then configure:
    <VeriPdfProvider config={{ worker: { src: '/pdf.worker.min.mjs' } }}>

6. Blank Viewer / CJK Text Not Rendering

Symptom: The viewer loads but some pages are blank or CJK characters display as squares/boxes.

Cause: CMap and standard font files are not served by your application.

Fix: Copy CMap files from pdfjs-dist to your public directory:

cp -r node_modules/pdfjs-dist/cmaps public/cmaps
cp -r node_modules/pdfjs-dist/standard_fonts public/standard_fonts

7. "structuredClone is not defined"

Symptom: ReferenceError: structuredClone is not defined at runtime.

Cause: Your browser or Node.js version is too old. structuredClone is available in Node.js 17+ and all modern browsers from 2022+.

Fix: Upgrade to Node.js 20+ or a supported browser. There is no polyfill path for structuredClone in VeriPDF.


8. Memory Leak in Single-Page Applications

Symptom: Memory usage grows over time when navigating to and from pages with VeriPDF viewers.

Cause: The viewer's React root, event listeners, and worker blob URLs are not cleaned up on navigation.

Fix: Always call destroy() when removing a vanilla viewer:

// In a SPA lifecycle hook:
viewer.destroy()

For React, ensure the viewer is inside a component that unmounts properly. If using the vanilla API in a React useEffect, return a cleanup function:

useEffect(() => {
  const v = VeriPdf.create(containerRef.current!)
  v.load(buffer, 'doc.pdf')
  return () => v.destroy()
}, [])

9. Multiple Viewers Conflict with Theme

Symptom: Two viewers on the same page show different themes, or toggling theme in one viewer affects the other.

Cause: The theme system sets data-theme on the <html> element, which is global. Multiple VeriPdfProvider instances with different theme modes will conflict.

Fix: Use a single VeriPdfProvider wrapping all viewers, or ensure all viewers use the same theme mode.


10. Signatures Show "Not Checked (offline)" for Revocation

Symptom: revocationCheck.status is "unknown" and revocationCheck.detail says "Not checked (offline)."

Cause: The base @trexolab/verifykit-core does not perform online revocation checking. It only reads embedded CRL/OCSP data from the PDF.

Fix: Install and configure the revocation plugin:

npm install @trexolab/verifykit-plugin-revocation
import { revocationPlugin } from '@trexolab/verifykit-plugin-revocation'
 
const verifier = await createVerifier({
  plugins: [revocationPlugin()],
})

11. "VeriPdf viewer failed to initialize within 10 seconds"

Symptom: Timeout error when calling viewer.load() on a vanilla instance.

Cause: The internal React root did not mount in time. This can happen if the container element is not visible, is detached from the DOM, or if the WASM module failed to load silently.

Fix:

  • Ensure the container element is attached to the document and visible before calling VeriPdf.create().
  • Check the browser console for WASM loading errors.
  • Verify that .wasm files are accessible from your hosting environment.

12. TypeScript "Cannot find module '@trexolab/verifykit-react/styles.css'"

Symptom: TypeScript reports an error on the CSS import.

Cause: TypeScript does not know how to resolve .css imports by default.

Fix: Add a CSS module declaration to your project:

// src/global.d.ts
declare module '*.css' {}

Or use the types export provided by the package -- ensure your tsconfig.json includes "moduleResolution": "bundler" or "node16".