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 other ld+json script in the file is inert content.
  • The node’s @type is Book; readers must not require it.
  • @context is the two-entry array above; meta.vocab entries 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 from JSON.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-urlsourceUrl), except browser/tooling directives (viewport, generator, theme-color, color-scheme, robots, referrer, …), property= statements, data-spub-meta rows (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.