TPTruPDF

@trupdf/security

DRM, watermark, audit log, structural redaction.

DRMEngine

Permission gate. Doesn't encrypt — that's the secure-mode + structural- redaction job. enforce(permission) throws on denial.

const drm = new DRMEngine({
  permissions: new Set(['view', 'print']), // 'view'|'print'|'copy'|'edit'|'export'|'annotate'|'redact'|'sign'
  expiresAt: '2026-12-31T23:59:59Z',
  maxOpens: 10,
});
drm.enforce('print');

RedactionEngine

Structural removal of content from the PDF byte stream, audited and non-cancellable. There is no visual-only fallback — if the structural adapter is unreachable, the burn refuses.

import { RedactionEngine, AuditLogger, bindPdfboxRedactionAdapter } from '@trupdf/security';
 
// Call once at app boot. Wires the engine to the TruPDF document
// service (the self-hosted container that powers structural redaction
// and full ISO 19005 PDF/A validation).
bindPdfboxRedactionAdapter({
  endpoint: process.env.NEXT_PUBLIC_TRUPDF_DOCSERVICE_URL!,
  internalSecret: process.env.NEXT_PUBLIC_TRUPDF_DOCSERVICE_SECRET, // optional auth gate
});
 
const audit = new AuditLogger();
const redactor = new RedactionEngine();
const result = await redactor.apply(
  srcBytes,
  [{ page: 4, x: 100, y: 200, width: 80, height: 14, exemptionCode: 'FOIA b6' }],
  { audit },
);

RedactionMark fields: page, x, y, width, height, plus optional exemptionCode, overlayText, color, opacity for the drawn indicator box.

The audit entry is appended before the bytes are returned. If the audit append throws, the burn aborts and the original bytes are left untouched.

Custom adapters

Hosts that prefer their own structural-removal backend register via registerStructuralRedactionAdapter:

import { registerStructuralRedactionAdapter } from '@trupdf/security';
registerStructuralRedactionAdapter(async () => ({
  async removeIntersectingObjects(bytes, marks) {
    // structurally remove content intersecting the marks
    return { bytes: rewritten, removed: opsRemoved };
  },
}));

registerPdfiumAdapter is an exported alias of the same function — use it when binding a native-engine (WASM) removal backend.

From the viewer

ViewerEngine exposes applyRedactions() which collects every redaction-mark annotation, calls the engine, replaces the loaded document with the burned bytes, and removes the marks from the annotation manager.

const result = await engine.applyRedactions({ actor: currentUser });
console.log(result.objectsRemoved);

The TruPDF document service

The reference adapter targets the TruPDF document service — a self-hosted container shipped with the distribution. Its burn pipeline runs a two-pass content-stream rewrite: pass 1 tags every text-showing operator whose glyph bbox intersects a redaction rect; pass 2 rewrites the page content stream removing those operators. A solid black rectangle is then drawn on top as a visual indicator, so the output is verifiably clean in any viewer.

docker compose --profile phase8 up -d

If the service runs with its shared-secret auth gate enabled, pass the matching secret via the adapter's internalSecret option (sent as X-TruPDF-Internal-Secret). A secret shipped to a browser app is visible to end users — front the service with your own authenticated proxy for stronger control.

Watermark

applyWatermark(input, { text, fontSize? = 60, rotation? = 30, opacity? = 0.18, color?, pages? }) draws into the page stream so the watermark survives flattening.

AuditLogger

Append-only event log. Events are frozen; subscribers forward to backend storage via the appended event.