Skip to content

v1.4.0 — PAdES B-T/B-LT/B-LTA, compare, metadata & manifest pipelines - #40

Merged
Nizoka merged 3 commits into
mainfrom
release/v1.4.0
Aug 26, 2026
Merged

v1.4.0 — PAdES B-T/B-LT/B-LTA, compare, metadata & manifest pipelines#40
Nizoka merged 3 commits into
mainfrom
release/v1.4.0

Conversation

@Nizoka

@Nizoka Nizoka commented Aug 26, 2026

Copy link
Copy Markdown
Owner

v1.4.0 — PAdES B-T/B-LT/B-LTA, compare, metadata & manifest pipelines

Branch: release/v1.4.0 → main
Type: Minor release (additive, 100% backward-compatible command surface with v1.3.0)
pdfnative bump: ^1.6.0 → ^1.7.0
Support policy: Node.js ≥ 22 (was ≥ 20 — Node 20 EOL 2026-04-30); CI matrix 22 + 24

Summary

  1. Completes the ROADMAP "Next" section: sign-side LTV. sign --timestamp <tsa-url>
    (RFC 3161, PAdES B-T) is now functional; new ltv collect|embed|add command writes
    /DSS + /VRI (B-LT, with an air-gapped collect→embed flow); new doc-timestamp
    command appends /DocTimeStamp revisions (B-LTA).
  2. Two more new commands: metadata (incremental /Info + XMP updates that keep
    signatures valid) and compare (text/structure diff with CI exit codes).
  3. Multi-signature support end-to-end: sign --allow-multiple/--field-name/--profile/ --digest/--signature-rect/--signature-page/--placeholder-bytes, inventoried by
    inspect --signatures, validated per field by verify (incl. SHA-384/512 and
    /DocTimeStamp token validation).
  4. batch --manifest tasks.json — declarative multi-command pipelines with @id output
    references; network inside a manifest requires --allow-network.
  5. render: --strict PDF/A diagnostics, JSON-usable image blocks (src/dataBase64),
    print production (bleed/boxes/marks/userUnit/ICC output intent), viewer print
    preferences, charts v2 (9 kinds, dual axes, log/time), --chunk-size,
    params.metadata (+trapped).
  6. Agent surface: new stable code E_NETWORK, schema subjects 15 → 19 (ltv-data,
    compare, batch-manifest, metadata), global --max-inflate-size, completions +
    manifest + llms.txt for all 21 commands.
  7. Fixes a long-standing inspect bug (signature/form-field counters always 0).
  8. veraPDF integration: the CLI's PDF/A claims are now validated against the veraPDF
    reference validator — a 12-file CLI-generated corpus (10 positive + 2 negative
    canaries) checked in a blocking CI workflow and again pre-publish; local gate
    via npm run validate:pdfa (exit 0 without veraPDF = skip, not a pass). The PDF/A
    samples themselves are now rendered actually-conformant (--font latin --lang latin
    via run-all.js).

Changes

package.json

  • version 1.3.0 → 1.4.0; pdfnative ^1.6.0^1.7.0; engines.node >=20>=22.
  • Description + keywords extended (pdf-compare, document-timestamp, dss, pades-lt/lta,
    print-production, …). Security overrides refreshed (js-yaml ^4.3.1, nanoid ^3.3.18).

src/core-bridge/index.ts

  • Selective re-exports for the 1.7.0 surface: signPdfBytesWithTimestamp,
    estimateContentsSize, collectValidationInfo / embedValidationInfo /
    addValidationInfo / vriKeyForContents, addDocumentTimestamp, listSignatures,
    timestamp/revocation provider get/set, RFC 3161 token parsers,
    setMaxInflateOutputSize, DER/hash/RSA primitives for the offline mock PKI, and all
    associated types (print production, diagnostics, viewer preferences, metadata update,
    image blocks).

New commands

  • src/commands/ltv.ts — collect (network, --online mandatory) / embed (offline) / add;
    serialises LtvData as versioned base64 JSON (schema subject ltv-data).
  • src/commands/docTimestamp.tsdoc-timestamp (B-LTA), --url mandatory opt-in.
  • src/commands/metadata.ts — incremental updateMetadata (signatures preserved).
  • src/commands/compare.ts — text/structure diff, E_CHECK_FAILED on differences.

Enhanced commands

  • sign — functional --timestamp (+--timestamp-digest, --timestamp-nonce),
    multi-signatures, --profile, --digest, visible-signature placement, placeholder
    sizing via estimateContentsSize(..., { timestamp: true }). No flags → byte-identical
    1.3.x path.
  • verify — RSA SHA-384/512 OIDs, per-signer digest for the byte-range hash,
    /DocTimeStamp validation (imprint + token signature), additive fieldName /
    isDocTimestamp.
  • inspect--signatures, page boxes + /UserUnit, metadata.trapped,
    --check "signatures>=N"; fixed name-object comparisons in the legacy counters.
  • annotate--password (encrypted incremental updates).
  • render--strict + diagnostics routing, image-block resolution, --chunk-size,
    ICC outputIntent revival from JSON (number[] → bytes).
  • batch--manifest / --allow-network / --continue-on-error (validation before
    execution, whitelist, @id graph, fail-fast).
  • Global --max-inflate-size (src/index.ts, dynamic import keeps startup fast).

New / changed utilities

  • src/utils/tsa.tscreateTsaProvider(url) : RFC 3161 POST over the existing SSRF
    guard (E_NETWORK on failure, response bodies never echoed).
  • src/utils/ltv-provider.tscreateRevocationProvider() : OCSP POST / CRL GET over
    the same guard.
  • src/utils/manifest.ts — pure manifest parsing/validation/@-resolution/network policy.
  • src/utils/keys.ts — native crypto provider gains per-call digest selection
    (sha256/384/512).
  • src/utils/layout.ts — revives outputIntent.iccProfile from JSON (number[]
    bytes) so ICC output intents are reachable from --layout files.
  • src/utils/cms-verify.ts, src/utils/timestamp-verify.ts — digest agility +
    verifyDocTimestamp().
  • src/utils/error.ts / agent.tsE_NETWORK (auto-published in schema manifest).

Wiring (single source of truth respected)

  • src/index.ts — USAGE 17 → 21 (+ 4 *_USAGE blocks, help switch, loadCommand).
  • src/commands/completion.ts — 4 new commands + all new flags (drives 4 shells + the
    capability manifest).
  • src/commands/schema.ts — subjects 15 → 19 + extended render/inspect/verify/batch/
    status schemas.

Samples

  • New pairs (.sh + .ps1, offline by default; network steps gated on
    PDFNATIVE_TSA_URL): sign/06-timestamp (replaces 06-timestamp-reserved),
    sign/08-ltv (full PAdES ladder), sign/09-multiple-signatures,
    inspect/08-list-signatures, metadata/01-update-metadata, compare/01-compare,
    batch/03-manifest (+ manifest/tasks.json).
  • New render JSONs (auto-discovered by run-all.js): render/print/01-bleed-marks,
    render/print/02-viewer-prefs, render/chart/03-stacked-bars,
    render/chart/04-area-scatter, render/chart/05-time-axis.

scripts/ & workflows (veraPDF PDF/A gate)

  • scripts/generate-pdfa-corpus.mjs — drives the built CLI to write a 12-file
    PDF/A corpus to test-output/pdfa/ + manifest.json: 10 positive entries
    (--strict --font latin --lang latin across 1b/2b/2u/3b, attachments,
    headers/footers, outline, opaque watermark, incremental PAdES sign, incremental
    metadata) and 2 negative canaries veraPDF must reject (no-fonts render —
    ISO 19005-2 §6.2.11.4.1; --variant table — ISO 19005-1 §6.3.4, the table path
    cannot embed fonts from the CLI).
  • scripts/validate-pdfa.mjs — validates each file against its claimed XMP profile
    with veraPDF and compares with expectCompliant. Outcomes PASS/FAIL/XFAIL/XPASS/
    INFRA/SKIP; exit 0 ok/skip · 1 conformance (incl. fatal XPASS + coverage canary) ·
    2 no corpus · 3 INFRA. VERAPDF_REQUIRED=1 fail-closed; VERAPDF_HOME,
    VERAPDF_REPORT_DIR supported; Windows .bat launcher handled.
  • package.json — new scripts corpus:pdfa and validate:pdfa.
  • .github/workflows/verapdf.ymlblocking (no continue-on-error), pinned
    veraPDF 1.30.2 installer with SHA-256 verified before java -jar, report + raw
    XML uploaded as artifact and rendered in the job summary.
  • .github/workflows/publish.yml — the same veraPDF gate repeated pre-publish.
  • Zero npm dependencies added — veraPDF is an external tool, never bundled.

Docs

  • README (What's new, Highlights, Supported Features group, "PDF/A status" callout,
    Quick Start, 21-command reference, Node ≥ 22), KNOWLEDGE_BASE (§2/4/5/6/8/9/10),
    CONTRIBUTING.md (new "PDF/A validation (veraPDF)" section: scripts, exit codes,
    skip semantics, install recipes, PR checklist), CLAUDE.md, AGENTS.md, llms.txt, ROADMAP
    (v1.4.0 released; Next cleared; deferred items recorded), samples/README,
    CHANGELOG, release-notes/v1.4.0.md, CITATION.cff re-synchronised (was 1.2.0 / "six
    composable commands"), CI matrix 22/24.
  • Factual corrections: svg blocks were wrongly documented as non-JSON-usable;
    math was missing from the render --font list.

Tests

  • tests/helpers/der.ts + tests/helpers/mock-pki.ts — offline mock PKI (root CA,
    signer with AIA/CRL-DP URLs, TSA, OCSP responder) issuing genuine DER structures,
    ported from pdfnative's own unpublished test helper; validated against the library's
    parsers.
  • New/extended suites: sign (timestamp + multisig), ltv (+doc-timestamp), verify
    (digests + DocTimeStamp), inspect/annotate/pagetree (boxes preservation), render
    (charts v2 / print / strict / images), metadata, compare, batch manifest, schema.
  • 600 tests, 40 files, all green (452 in v1.3.0). Coverage above the enforced
    thresholds (statements 79 / branches 68 / functions 83 / lines 79 — unchanged).

Independent audit (this release)

  • V1 — double-blind gap analysis (2 agents) before implementation: full pdfnative
    ≤ 1.7.0 public surface vs planned CLI surface. Consensus finding (both auditors):
    image blocks were the only generation capability unreachable from the CLI → fixed.
    Additional accepted findings: verify/digest coherence, /DocTimeStamp validation,
    annotate --password, print-box inspection, visible-signature flags,
    --max-inflate-size, render --chunk-size, two documentation corrections. Deferred
    to ROADMAP: custom TTF loading, link annotations on existing PDFs, doctor language
    enumeration.
  • V3 — post-implementation conformance review: two independent reviewers (factual
    accuracy of docs vs code; 2026 open-source standards) plus an arbiter judging finding
    legitimacy; accepted findings applied. (Reports summarised in this PR's discussion.)

Validation

  • npm run typecheck:all — clean · npm run lint — 0 errors ·
    npm run test:coverage — 600/600, thresholds met · npm run build — ok ·
    npm audit --audit-level=high — 0 vulnerabilities.
  • Built-binary smoke (node dist/cli.cjs): --version = 1.4.0, --help,
    schema manifest (21 commands, E_NETWORK), completions include the new commands,
    and an end-to-end render → metadata → compare → inspect --signatures round-trip.
  • node samples/run-all.js green; new .sh/.ps1 samples executed offline on
    Git Bash + PowerShell.
  • npm run validate:pdfa with veraPDF 1.30.2 installed locally: 10 PASS +
    2 XFAIL
    (both negative canaries correctly rejected by the validator), exit 0.
  • Zero-network guarantee in tests: mock providers injected via
    setTimestampProvider/setRevocationProvider, RFC 2606 .invalid URLs.

Backward compatibility

  • No existing flag, default, exit code, error code or envelope changed. All new JSON
    fields are optional/additive; sign without new flags follows the 1.3.x code path
    byte-for-byte.
  • sign --timestamp was a reserved flag whose error message announced future
    availability — activating it is the documented contract, not a break.
  • Schema $ids embed the CLI version and moved 1.3.0 → 1.4.0 (expected, pinned by
    tests).
  • Node ≥ 22 is a support-policy change (EOL alignment + upstream engines), not an API
    change.
  • Inherited pdfnative 1.7.0 byte-level changes (forms /ToUnicode, RTL fixes, box
    preservation) are documented in the CHANGELOG.

Out of scope (recorded in ROADMAP)

  • optimize (linearisation/recompression) — still blocked upstream.
  • Visual compare — no rasteriser upstream.
  • Arbitrary object modifymetadata covers the metadata slice only.
  • render --font-file (custom TTFs), link annotations on existing PDFs, doctor
    language-pack enumeration, dedicated TSA timeout flags.

Self-review checklist

  • npm run typecheck:all clean
  • npm run lint 0 errors
  • npm run test:coverage green, thresholds unchanged and met
  • npm run build + built-binary smoke test (node dist/cli.cjs --help, new
    commands, schema manifest)
  • CHANGELOG.md updated (Keep a Changelog)
  • No breaking change to the machine contract (envelopes, exit codes, E_*)
  • No new runtime dependency (pdfnative remains the only one)
  • Docs + samples + completions + schemas cover the whole 21-command surface
  • No autonomous GitHub writes — this draft is committed for human review (HITL)

Nizoka added 3 commits August 26, 2026 02:57
…ta & manifest pipelines

Built on pdfnative 1.7.0. Completes the ROADMAP "Next" section (sign-side LTV)
and grows the CLI from 17 to 21 commands. 100% backward-compatible command
surface with v1.3.0; support policy moves to Node.js >= 22 (Node 20 EOL
2026-04-30; upstream engines requirement).

New commands:
- ltv (collect|embed|add) — PAdES B-LT /DSS + /VRI; --online opt-in for
  network, replayable ltv-data JSON, fully offline embed (air-gapped)
- doc-timestamp — PAdES B-LTA /DocTimeStamp revisions (repeatable renewal)
- metadata — incremental /Info + XMP updates that keep signatures valid
- compare — text/structure diff with CI exit codes (E_CHECK_FAILED)

Enhancements:
- sign: functional --timestamp (RFC 3161, B-T) + --timestamp-digest/-nonce,
  multi-signatures (--allow-multiple/--field-name), --profile pkcs7|pades,
  --digest sha256/384/512, visible placement (--signature-rect/-page),
  --placeholder-bytes
- verify: RSA SHA-384/512, /DocTimeStamp token validation, fieldName +
  isDocTimestamp per signature
- inspect: --signatures inventory, print boxes + /UserUnit, metadata.trapped,
  --check "signatures>=N"
- annotate: --password (encrypted incremental updates)
- render: --strict PDF/A diagnostics (+diagnostics[] envelope), image blocks
  from JSON (src/dataBase64), print production, ICC output intents, viewer
  print preferences, charts v2 (9 kinds), --chunk-size, params.metadata
- batch: --manifest pipelines (@id refs, 14-command whitelist, size/task caps,
  path-traversal parity, --allow-network gate, --continue-on-error)
- Agent surface: E_NETWORK error code, schema subjects 15 -> 19, global
  --max-inflate-size, completions/manifest/llms.txt for all 21 commands

Fixes:
- inspect signature/form-field counters were always 0 (PDF name comparison)

Security: all new network paths (TSA/OCSP/CRL) are explicit opt-ins through
the existing SSRF guard; offline mock PKI (genuine RFC 3161/OCSP/CRL DER) keeps
the whole PAdES ladder tested with zero network. SECURITY.md rewritten for the
new surface. Release validated by a multi-agent audit chain: double-blind gap
analysis, factual + standards reviews, and an arbitration pass (reports
summarised in release-notes/draft/PR-v1.4.0.md).

Docs: README, KNOWLEDGE_BASE, AGENTS, CHANGELOG, ROADMAP, llms.txt, SECURITY,
CITATION.cff, samples/README, release-notes/v1.4.0.md + draft PR; new samples
as .sh/.ps1 pairs (offline by default, TSA steps via PDFNATIVE_TSA_URL).

Bumps pdfnative ^1.6.0 -> ^1.7.0; version -> 1.4.0; engines >= 22; CI 22/24.
600 tests, npm audit clean.
…es, CI + pre-publish

Ports the veraPDF dispositif from pdfnative-mcp (v2: manifested corpus,
negative canaries, fail-closed INFRA handling) and makes it BLOCKING from
day one like pdfnative (CI + pre-publish gate).

- scripts/generate-pdfa-corpus.mjs: drives node dist/cli.cjs over the PDF/A
  sample specs into test-output/pdfa/ (12 files: 10 positives incl.
  incremental sign --profile pades and metadata update; 2 negative canaries
  with expected ISO clauses 19005-2 6.2.11.4.1 and 19005-1 6.3.4).
- scripts/validate-pdfa.mjs: faithful port — exit 0 ok/skip, 1 conformance,
  2 corpus, 3 INFRA; VERAPDF_REQUIRED=1 fail-closed; VERAPDF_REPORT_DIR;
  hardened XML parsing; XPASS fatal; coverage + negative-canary guards.
- npm scripts: corpus:pdfa, validate:pdfa (zero new dependencies — veraPDF
  is an external CI tool, never bundled).
- .github/workflows/verapdf.yml: blocking, pinned installer 1.30.2 with
  SHA-256 verified before java -jar, artifacts + job summary.
- publish.yml: same hardened gate before npm publish.
- samples: run-all.js now renders pdfa/attachments with --font latin --lang
  latin (previously the PDF/A samples shipped without embedded fonts — not
  ISO 19005 conformant) and surfaces warnings on success; sample prose and
  samples/README corrected.
- Docs: README PDF/A status, CONTRIBUTING veraPDF section (+Node>=22 and
  real coverage thresholds), KNOWLEDGE_BASE, AGENTS, CLAUDE.md (network
  philosophy updated for the 1.4.0 opt-ins), llms.txt, CHANGELOG 1.4.0,
  release notes + PR draft.

Validated locally with veraPDF 1.30.2: 10 PASS + 2 XFAIL (expected
clauses), 0 FAIL/XPASS/INFRA, exit 0. 600 tests green, npm audit clean.
…sh node fix)

Applies the 5 legitimate findings from the final two-controller + arbiter
review of the v1.4.0 release:

- CHANGELOG: document the corrected counting basis of `inspect --check
  signed` (placeholders and /DocTimeStamp revisions no longer count — a
  placeholder-only PDF passed in 1.3.0 and now fails; correctness fix).
- CONTRIBUTING: exact exit-3 semantics of validate-pdfa.mjs (per-file INFRA
  is always fatal; only "veraPDF absent" is gated by VERAPDF_REQUIRED) and
  corpus:pdfa does not build (needs a prior npm run build) — same fix in
  KNOWLEDGE_BASE.
- verapdf.yml: trigger paths gain tsup.config.ts + tsconfig.json (the gate
  validates the tsup-built dist).
- ROADMAP: veraPDF gate recorded in the v1.4.0 section; deferred items
  traced (table-variant font-embedding gap, setup-verapdf composite action,
  failed-rule regex robustness).

Both controllers and the arbiter confirm: publishable, zero breaking change
vs 1.3.0, project philosophy respected on all six principles.

- publish.yml: node-version '22.x' -> '>=22.14.0' — npm Trusted Publishing
  (token-less OIDC) requires npm >= 11.5.1, which ships with the newest Node
  the range resolves to; '22.x' would pin bundled npm 10.9 and break the
  publish. Mirrors the sibling pdfnative workflow; the floor matches the
  >= 22 support policy.
- release-notes/draft/PR-v1.4.0.md: attribution footer removed.
@Nizoka Nizoka self-assigned this Aug 26, 2026
@Nizoka Nizoka added bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request labels Aug 26, 2026
@Nizoka
Nizoka merged commit 32d0dd8 into main Aug 26, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant