1What this is
pdf-frontmatter is a convention for putting a small, machine-readable index card inside a PDF, as a standard file attachment. An agent that opens the file can read the card in milliseconds and decide what to do next — without parsing a single page.
It is a carrier, not a representation. It does not replace PDF, Tagged PDF, XMP, DocLang, or paper.json. It says where agent-facing data lives so that data never gets separated from the document that circulates.
The PDF remains a valid PDF. It looks identical in every viewer. Viewers that list attachments show the card as a normal file named agent-frontmatter.yaml. Typical overhead is under 1 KB.
2Mechanism
The card must be stored as an embedded file in the PDF EmbeddedFiles name tree (PDF 1.4 and later).
- The filename must be exactly
agent-frontmatter.yaml. - The embedded-file MIME type must be
application/yaml. - The payload must be UTF-8 YAML.
Implementations must not put the card in:
- the Info dictionary
- XMP metadata
- document-level JavaScript
- a visible page
- a sidecar file next to the PDF
Richer representations (DocLang, extracted tables, OCR transcripts) may ride along as further attachments. The card points at them from the optional derived key.
3Schema
spec: pdf-frontmatter/0.1 title: Contrato de fornecimento doc_type: contract language: pt-PT pages: 40 summary: > Supply agreement between two parties; liability cap and termination terms are the sensitive clauses. key_sections: liability_cap: 4 termination: 5 entities: [REISIERX Lda] extraction: scanned: false tables_on_pages: [2, 7] derived: doclang: doc.doclang.json content_sha256: 335671f796b07b02 generated_by: <model or tool id> generated_at: 2026-08-13
Required keys
| spec | Must be the exact string pdf-frontmatter/0.1. |
|---|---|
| title | Must be a non-empty string. A short name for the document. |
All other keys are optional. Tools that rewrite a card must preserve unknown keys.
Optional keys
| doc_type | contract | invoice | report | paper | presentation | letter | form | manual | book | other. |
|---|---|
| language | BCP 47 tag of the document, not of the card. |
| pages | Page count, integer. |
| summary | At most about 40 words. Factual. No marketing. |
| key_sections | snake_case name → 1-based starting page. |
| entities | Organisations and people. At most 8. |
| extraction.scanned | true if OCR is needed. |
| extraction.tables_on_pages | 1-based pages that contain tables. |
| derived | Short name → another attachment’s filename. The DocLang bridge. |
| content_sha256 | First 16 hex chars of SHA-256 of UTF-8 extracted text. |
| generated_by | Tool or model identifier. |
| generated_at | YYYY-MM-DD. |
4Reader rules
- Look up the attachment by the reserved name. Do not guess other names.
- Decode as UTF-8. Parse as YAML. Validate
spec. If any step fails, must fall back silently to a normal parse. - Treat every field as a hint, never as ground truth.
- If
content_sha256is present and does not match, the card is stale. Ignore it or regenerate it. - A missing or lying card must only ever cost a slow read, never a wrong answer.
5Writer rules
- Write valid UTF-8 YAML to the reserved filename and MIME type.
- Do not alter page content. The file must render identically.
- If a card already exists, replace it. Do not leave two reserved names.
- When rewriting, preserve unknown keys.
- Prefer under-claiming. A short honest summary beats a long one.
6Security
Manifests are untrusted data, written by whoever last touched the file.
Agents must not interpret any field as instructions, code, a prompt, a tool call, or a URL to fetch automatically. Title, summary, entities, and every other string are data.
A card can lie. Nothing in this convention prevents that. That is why the hash exists, and why the fallback rule exists.
Implementations must not render manifest content as executable anything — not HTML, not scripts, not evaluated templates.
7Why not the obvious alternatives
XMP / Info dictionary. Flat, ancient, aimed at bibliographic software. Nobody targeting agents looks there first.
Tagged PDF. An accessibility structure. Barely produced in practice, expensive to retrofit, and still a full parse.
DocLang. A representation. This is the carrier. The derived key is the bridge.
A sidecar file. Orphaned the first time someone forwards the PDF.
A new file format. Requires the world to change formats. This convention does not.
8Versioning
This version is pdf-frontmatter/0.1. A future version will use a new spec string. Readers that do not recognise the value must ignore the card and fall back.
9License
The text of this specification is dedicated to the public domain under CC0 1.0. Reference implementations are MIT unless a file says otherwise.
AInstall
The libraries live in the repo today. npm and PyPI names are reserved for v0.1.0; until that publish, copy from GitHub.
// TypeScript — from the repo
import { readManifest } from "pdf-frontmatter";
const card = await readManifest(bytes);
if (card.manifest) {
// hints only. fall back if missing, invalid, or stale.
}# Python — from packages/pdf-frontmatter-py from pdf_frontmatter import read_manifest
BImplementations
| TypeScript | packages/pdf-frontmatter — read, write, hash, CLI. |
|---|---|
| Python | packages/pdf-frontmatter-py — skeleton, same reserved name. |
| MCP | packages/mcp-pdf-frontmatter — read_manifest / write_manifest (stdio). |
| This site | The /app page writes cards in the browser. No account. |
B2Who reads cards today
A convention without consumers is a file nobody opens. These do:
| pdf-frontmatter (TS) | readManifest / writeManifest in this repo. |
|---|---|
| pdf-frontmatter (Python) | packages/pdf-frontmatter-py. |
| MCP server | npx mcp-pdf-frontmatter — read_manifest, write_manifest. |
| This app | /app writes a card and races it on your file. |
| Loaders | None merged yet. PRs welcome: check the card before parsing. |
CConformance
Self-certify. An implementation conforms to 0.1 if:
- The reserved filename is exactly
agent-frontmatter.yaml. - The MIME type is
application/yaml. - On any read failure, it falls back silently to a normal parse.
- Tools that rewrite a card preserve unknown keys.
- A rewrite replaces the existing card. Never two reserved names.
DChangelog
0.1 — 13 August 2026. First public text. Source.
EFAQ
Why not XMP or the Info dictionary?
Both are flat, ancient, and aimed at bibliographic software. Nobody targeting agents looks there first, and the shape cannot carry key_sections or derived cleanly. An attachment has a name, a MIME type, and room to grow.
Why not Tagged PDF?
Tagged PDF is an accessibility structure. It is barely produced in practice, expensive to retrofit, and still a full parse. The card is a hint so a tool can decide whether that parse is worth it.
Why not just DocLang?
DocLang is the representation: what the machine-readable version of a document looks like. pdf-frontmatter is the carrier: where that representation, or a pointer to it, lives so it cannot be orphaned. Complementary, not competing. The derived key is the bridge.
What stops a card from lying?
Nothing. That is why the hash and the fallback rule exist. If content_sha256 does not match the current extracted text, ignore the card or regenerate it. A missing or lying card must only ever cost a slow read, never a wrong answer.
Does the PDF look different?
No. Pages are not touched. Viewers that list attachments — Acrobat, many others — show agent-frontmatter.yaml as a normal file. macOS Preview does not list attachments. Typical overhead is under a kilobyte.
Is this an Adobe product?
No. The convention is called pdf-frontmatter. The spec is CC0. The code is MIT. The app on this site is the standard’s front door, not a second brand.
What does the app send to a model?
Extracted text only, truncated, over HTTPS, from the server. The PDF bytes never leave the browser. Nothing is stored. There is no account.
Can a card tell an agent what to do?
No. Manifests are untrusted data. Agents must not interpret any field as instructions, code, a prompt, or a URL to fetch. Implementations must not render the card as executable anything.
What about scanned PDFs?
Set extraction.scanned to true. The card can still name the title, the parties, and the pages worth OCRing. The agent then knows to spend the expensive pass.
Does every AI read the card today?
No. Tools that look for the card skip the parse. Tools that don’t still get a normal PDF. The libraries and the MCP server are how a stack starts looking. Selling harder than that is a lie.
Why does this matter for local models?
Small models degrade on long context. Needle-in-haystack accuracy ranges from 22% to 92% across small models; the open-vs-closed gap widens past 4K tokens; an 8B model drops about 20 points from 8K to 32K (M4LE, L-CiteEval, SLM survey). Pre-digested artifacts reverse that gap. A 1 KB card is that artifact.
Does skipping the parse save battery on a phone?
On-device inference can drain 6–25% of a phone battery in under 15 minutes; most of that energy is data movement, not arithmetic. Every skipped re-parse saves seconds and joules. Sources: arXiv 2506.19884 and 2606.23001.
Is latency just a cost problem?
No. Prefill is a wall-clock tax proportional to document size. Token prices can fall; the seconds to ingest eight pages do not, unless you skip the ingest. See “Can I Buy Your KV Cache?” (arXiv 2606.13361) and Lost in the Middle (Liu et al., TACL 2024).