Skip to content

pdf-trust — Incoming Audit ​

A Skill that audits whether an incoming PDF (contract, invoice, medical document, government filing, …) is genuine, trustworthy, and untampered, and returns a Trust Report with an explicit recommendation.

Principles ​

  1. Never judge the truth of the content — only authenticity (originality and integrity)
  2. Return verification results as technical facts; the final decision belongs to the user
  3. Always name the grounds for the verdict (which tool, which result)
  4. The verdict comes from code; the LLM only writes the explanation — the 4-value verdict is returned by pdf-verify's evaluate_policy. The Skill's job is explaining firedRules, recommending actions and citing legal grounds — never overriding the verdict

The 4-value verdict ​

VerdictMeaning
trust_and_useUsable as-is
use_with_cautionUsable with noted caveats
human_review_requiredNeeds human review
rejectNot acceptable

Required MCPs ​

MCPRequired / optionalRole
pdf-verify (v0.7.0+, v0.29.0+ recommended)Requiredevaluate_policy, signature verification, tamper detection, PAdES, PDF/A (v0.11.0+ adds PDF/A-4: pdfa-4 / pdfa-4e / pdfa-4f — pdfa-4b does not exist)
pdf-readerOptionalObserving signature-field structure, tags, metadata
pdf-specOptionalCiting ISO 32000 grounds for deviations
houki-egov / houki-nta / tax-law / labor-lawOptionalStatutory grounds required by the chosen profile

v0.7.0 is the floor because that is where evaluate_policy arrives at all. v0.21.0 is needed for the output shapes, because that release moved the top level of verify_signatures / detect_pades_level JSON from an array to an object ({ scope, signatures: [...] } / { scope, levels: [...] }) — reading only the list leaves you unable to notice that the list is incomplete. v0.29.0+ is what the Skill recommends. v0.27.0 judges certificate validity and revocation at the time of a verified timestamp (ISO 32000-2 §12.8.3.4.5 / §12.8.3.4.6): a revoked signer certificate is revoked_after_validation_time (use_with_caution) only when a timestamp proves the signature predates the revocation, and indeterminate otherwise — earlier versions returned invalid. v0.28.0 stops using revocation information older than revocation_freshness (default 24 hours) as evidence for good. v0.29.0 stopped cutting JSON at 25,000 characters, which could leave a body that does not parse under isError: false; a capped list now carries xxxTruncated: { returned, total }. pdf-trust v0.8.1 reads the 0.27.0 / 0.28.0 changes and v0.8.2 the 0.29.0 fields.

How an audit flows (Phases) ​

PhaseWhat happens
0Confirm the purpose, choose a profile
1Batch verdict over every file with evaluate_policy (4 values)
2Interpret the fired rules, dig deeper
2.5Identify the post-signing changes — lowering "N bytes were added" to "what was written, on which page, where"
3Profile-specific checks (PDF/A, PDF/UA, long-term preservation, …)
4Fetch statutory grounds (when the profile requires them)
5Produce the Trust Report

References like "Phase 2.5" in this site point at this table.

Profiles ​

ProfileIntended documentsBehaviour
contractContracts, NDAs, purchase ordersSignature required; unsigned → human_review_required
financialInvoices, financial statements, tax filingsLong-term-preservation checks included
legalLitigation materials, legal documentsRequires the full post-signing change history
medicalReferral letters, test reportsMost conservative; caution escalates to review
governmentGovernment documents, public noticesLong-term checks included; unsigned tolerated
generalEverything elseNo extra checks

Measured examples — all 4 verdicts (2026-08-11, real specimens) ​

SpecimenProfileVerdictFired rules
Self-made CA signature + CRL embedded in the DSS, trust anchor suppliedgeneraltrust_and_useNone (valid + trusted + revocation good)
Japanese official gazette, 2026-08-10 issue (Cabinet Office signature + AMANO timestamp)governmentuse_with_cautionTRUST-NOT-EVALUATED / REVOCATION-UNKNOWN
Same self-made specimen without the CRLgeneraluse_with_cautionREVOCATION-UNKNOWN
Unsigned invoice PDFcontracthuman_review_requiredUNSIGNED-REQUIRED (an image of a signature is not an electronic signature)
Specimen with one byte flipped inside the signed rangegeneralrejectPOL-REJECT-INVALID (digest mismatch)

Rows 1 and 3 differ only in the presence of a CRL (both had the trust anchor supplied). Supplying a trust anchor is not enough — the best verdict is reached only once revocation is confirmed good. With the CRL covering the signer, the PAdES structural observation also rises from B-T to B-LTA (the specimen carries a document timestamp from the start, so satisfying the B-LT condition with the CRL completes the B-LTA structure at the same time): one pair of specimens shows how the verdict and LTV relate.

Trust Report excerpt (from the gazette audit) ​

Beyond the verdict, Phase 2.5 (verify_integrity + locate_objects) identifies what the "+9,938 bytes after signing" actually are:

ObjectChangeRolePage / rect
64addedform field widgetp.1 / 0,0,0,0 (invisible)
65added/DocTimeStamp signature dictionarynot referenced by any page
54modifiedAcroForm dictionary—

→ The post-signing change is the application of the AMANO document timestamp itself (incremental updates are legal — ISO 32000-2 §7.5.6). Phase 2.5's job is lowering "N bytes were added" to "this is what was added". The same audit also recorded: veraPDF judged PDF/UA NOT COMPLIANT (236 untagged content items, 10 rules), and the reference PDF/A measurement was recorded as "not performed" because veraPDF returns no result for the encrypted document — an unmeasured check is never reported as passed.

Installation ​

sh
/plugin marketplace add shuji-bonji/claude-plugins
/plugin install pdf-verify-mcp@shuji-bonji   # required foundation
/plugin install pdf-trust@shuji-bonji
# optional: pdf-reader-mcp (locations), pdf-spec-mcp (clause citations), houki family (statutory grounds)

Repository: shuji-bonji/pdf-trust-skill (SKILL.md itself and the profile definitions)

Degraded operation ​

  • pdf-verify not connected → the audit cannot proceed; the Skill says so and stops
  • Old verify (before v0.7.0, no evaluate_policy) → falls back to the manual verdict table and labels the report "manual verdict"
  • Optional MCP missing → the item is recorded as "not performed (tool not connected)" — silently dropping it would read as "checked, no findings"
  • A check that could not be measured (e.g. veraPDF cannot score an encrypted PDF for PDF/A) → recorded as not performed. Undecided is not innocent

MIT Licensed