Metadata
ScriptPub has one metadata surface: the JSON-LD island — a single
<script type="application/ld+json" id="scriptpub-meta"> … </script>
element in the head. Every key of the in-memory Meta
model spells as a term of that island’s one node; the config island never
carries metadata. The island is the first, base, and default source of all
work metadata.
The island speaks schema.org — but only a prepared vocabulary: the
CreativeWork/Book terms that map cleanly onto SPUB and EPUB, listed below.
ScriptPub-specific keys ride the spub: context
(https://scriptpub.org/ns#). The island never invents schema.org terms
outside the prepared set; terms it does not recognize are preserved verbatim
(see Additional properties).
<script type="application/ld+json" id="scriptpub-meta">
{
"@context": ["https://schema.org", { "spub": "https://scriptpub.org/ns#" }],
"@type": "Book",
"name": "A Room with a View",
"author": "E. M. Forster",
"publisher": { "@type": "Organization", "name": "Edward Arnold, London" },
"datePublished": "1908",
"inLanguage": "en",
"description": "A one-line description.",
"keywords": ["fiction", "novel", "Edwardian"],
"about": ["Humorous stories", "England — Fiction"],
"image": "assets/cover.jpg",
"copyrightNotice": "Public domain",
"spub:indexAs": "Room with a View",
"spub:added": "2026-08-18",
"spub:source": { "note": "…", "url": "https://…", "accessed": "2026-08-18T09:45:00.000Z" }
}
</script>
Island rules:
- One island at most, identified by
id="scriptpub-meta"; only it is authoritative — any otherld+jsonscript in the file is inert content. - The node’s
@typeisBook; readers must not require it. @contextis the two-entry array above;meta.vocabentries merge into the object member (extension vocabularies such as a package’s custom prefixes).- Writers escape
</as<\/inside string values so the script element cannot terminate early; readers get it back for free fromJSON.parse. - The island is omitted entirely when it would carry nothing beyond
@context/@type.
The model deliberately has no bibliographic entity hierarchy — no Work/Expression/Manifestation/Item wrappers. What it does have is bounded structure: an agent, a subject, or an identifier is one flat record whose qualifiers ride the record itself (below), and everything richer stays a flat statement.
The prepared vocabulary — native schema.org terms
| Island term | meta key |
Notes |
|---|---|---|
@id |
uri |
Canonical URL — the work’s stable public identity. |
name |
title |
Required (the one required key). |
alternateName |
internationalTitle |
A broadly recognized title outside the work’s own language. |
author |
author + author entities |
Strings are the display line; objects are structured agent records. |
translator |
translator + translator entities |
Same string/object split. |
contributor |
contributor entities | Objects only. |
publisher |
publisher entities | Objects only (default @type Organization). |
datePublished |
year |
An opaque string — "1908", "-400". |
dateModified |
modified |
RFC 3339 date this edition last changed. |
inLanguage |
language |
BCP-47; default "en". |
description |
description |
One-line description. |
keywords |
keywords |
An array on the wire; the comma-joined string in memory. |
about |
subjects |
Subject statements — strings, or DefinedTerm objects. |
identifier |
identifiers |
Strings, or PropertyValue objects. |
image |
cover (+ coverAlt) |
A path string, or an ImageObject { url, description } when alt text exists. |
license |
license |
URL or text. |
copyrightNotice |
copyright |
|
translationOfWork |
translationOf |
Reference to the original this document translates. |
accessMode, accessModeSufficient, accessibilityFeature, accessibilityHazard, accessibilitySummary |
schemaProps |
The EPUB accessibility set — EPUB a11y metadata already uses these schema.org terms. |
The spub: context
Keys with no clean schema.org term spell as "spub:<key>", any JSON value:
| Island term | meta key |
|---|---|
spub:indexAs |
indexAs — the title’s filing form. |
spub:authorIndexAs |
authorIndexAs — the author’s filing form (“Einstein, Albert”). |
spub:titleTranscription / spub:authorTranscription |
Latin transcriptions for non-Latin scripts. |
spub:credit, spub:draft, spub:draftOf, spub:contact, spub:notes |
The flat keys of the same names. |
spub:narration, spub:narrator |
Narration audio path and narrator name. |
spub:coverAlt |
Cover alt text when there is no cover path to carry it. |
spub:added |
RFC 3339 date the work entered the library carrying it. |
spub:preset |
The app-managed preset reference (a scriptpub: uri or URL). |
spub:source |
{ note?, url?, accessed? } — the source/sourceUrl/sourceAccessed trio, structured. |
spub:kind, spub:parts, spub:library |
RESERVED for the app’s structural keys (document kind, multi-file work settings, library settings) — native JSON objects. |
spub:<custom> |
Any additional meta key, preserved verbatim with its JSON value. |
Agent records (entities)
Beside the flat display line ("author": "E. M. Forster"), an agent may be a
structured record — the full bibliographic statement a package projects:
"author": [
"E. M. Forster",
{ "@type": "Person", "name": "Forster, E. M. (Edward Morgan)",
"spub:fileAs": "Forster, E. M.", "spub:role": "aut",
"spub:roleScheme": "marc:relators" }
]
An agent object carries @type (Person | Organization; the default is
Person, Organization for a publisher), name, and the spub: qualifiers
id, fileAs, transcription, role, roleScheme, and attributes (a
record of unrecognized source attributes). A single value needs no array.
In memory these are meta.entities — { relation, name, … } with relation
author | contributor | publisher | translator | narrator (narrator
entities spell under spub:narrator).
Subjects and identifiers
about entries are strings, or DefinedTerm objects when a vocabulary
qualifies them; identifier entries are strings, or PropertyValue objects:
"about": [ "Poetry",
{ "@type": "DefinedTerm", "name": "Tides — Poetry",
"spub:authority": "LCSH", "spub:term": "sh0001" } ],
"identifier": { "@type": "PropertyValue", "value": "urn:isbn:9780000000001",
"propertyID": "ISBN", "spub:primary": true }
spub:primary marks the identifier a package’s unique-identifier selects.
The lossless remainder — spub:statements
Everything a package projection carries that has no structured home above —
Dublin Core elements beyond the mapped set (type, format, relation,
coverage…), rendition:* and media:* properties, legacy OPF
<meta name content> metas, refinements of another statement, unknown
vocabularies — rides "spub:statements": an ordered array of
{ property, value, form?, id?, primary?, refines?, scheme?, authority?,
language?, direction?, attributes? }
Order is array position. refines names another record’s id (with or
without a leading #). Linked metadata (OPF <link> — licences, alternate
representations, metadata records, accessibility reports) rides
"spub:links": { href, rel[], id?, mediaType?, language?, properties?, refines?, attributes? }.
"spub:complete": true (meta.bibComplete) marks the structured groups +
statements/links as a complete package projection, not supplements —
exporters use them as metadata authority and add normalized fallbacks only
for target-required title, identifier, and language when absent.
Additional schema.org properties
Island keys outside the prepared set (and outside spub:) are preserved
verbatim through meta.schemaProps — read untouched, re-emitted untouched.
This is how the EPUB accessibility set travels, and how a hand-authored extra
term survives a rewrite. Validators may warn; writers never emit terms they
did not read or that the model does not define.
Derived head spellings
Four facts are restated as native head markup for browsers — derived write spellings, regenerated on serialize, exactly as selector-derived identity classes are:
| Spelling | From |
|---|---|
<title> |
title |
<html lang> |
language (resolved; default en) |
<link rel="canonical"> |
uri |
<link rel="icon"> |
cover |
The body never carries machine-readable metadata — no microdata, no RDFa. A title page that displays the title and author is presentation, and its text may freely duplicate what the island states; the island alone is what machines read.
The fallback tier — files with no island
A file with no #scriptpub-meta island (plain foreign HTML, an un-migrated
head) still yields metadata from its native head markup, generously:
<title>→title;<html lang>→language;<link rel="canonical">→uri;<link rel="icon">→cover; a bare<link rel="license">→license;- repeated
<meta name="author">→author; - every other named
<meta name="…" content="…">→ the meta key of its camelized name (source-url→sourceUrl), except browser/tooling directives (viewport,generator,theme-color,color-scheme,robots,referrer, …),property=statements,data-spub-metarows (the retired v5 DC layer — migration tooling’s job), and bare Dublin Core element names.
When the island is present it is the only source: native tags are ignored.
Source provenance
meta.source is a human-readable bibliographic or adaptation note.
sourceUrl is the stable HTTP(S) landing page for the primary source used to
prepare the rendition, and sourceAccessed is its RFC 3339 access time.
Rights and licence values remain separate assertions; using a source does not
import its licence automatically.
Package mapping
The structured groups + spub:statements/spub:links are designed to carry
a complete publication package (EPUB OPF) projection losslessly: agents with
file-as/role refinements fold into entity records, subjects and
identifiers into theirs, plain schema:* properties into native island
terms, and everything else — all fifteen Dublin Core elements,
dcterms:modified, refinements, rendition and media-overlay properties,
legacy name/content metadata, ids, schemes, unknown attributes — into
statements and links in source order. An exporter reverses the mapping,
re-expanding entity records into dc: elements with EPUB 3 refinements. The
conversion pipelines themselves are app tooling, not part of this
specification.