A machine-readable schema of Apache Tika's metadata keys, plus a registry-driven validator
(MetadataKeyValidator) that classifies any key as CLOSED / OPEN / TEMPLATE / UNKNOWN.
Scope: tika-core + the standard parser bundle. The heavier/optional parser families
(scientific, sqlite3, nlp, vlm) are not scanned — pulling their runtime deps (netcdf, grib,
opennlp, DL4J, sqlite-jdbc) into a build-time schema module isn't worth it. Their keys are the only
ones absent (e.g. sqlite3:, vlm:, grib:, netcdf:, ctakes:, NER_). MetadataCoverageTest
enforces this: any module declaring keys that is neither scanned nor on its explicit out-of-scope
list fails the build, so nothing escapes silently.
Every key Tika declares as a Property constant, plus the bounded digest cross-product
(X-TIKA:digest:<ALGORITHM>[:<ENCODING>], enumerated from DigestDef). Each record:
{ key, namespace, valueType, cardinality }.
Generated, never hand-edited. SchemaGenerator scans the parser classpath for classes that
declare a Property field, force-loads them, reads the global Property table, and writes stable
sorted JSON. MetadataSchemaTest regenerates in-memory and asserts it matches the committed file, so
the registry can never drift from the declarations.
Regenerate after adding/changing a Property or a PassthroughPrefix (writes all three files):
tika-metadata-schema/regen.sh
Installs the dependency modules, regenerates the registries via the forked-exec profile, sanity-checks
the key-count diff, and runs the gate tests — see .skills/metadata-schema.md for flags and the
manual steps this replaces.
The prefixes under which parsers mint file-controlled key names at runtime — names that are not
Property constants, so the individual keys cannot be enumerated (scraped HTML <meta> under
html:, OOXML custom:, email Message:Raw-Header:, Access MDB_PROP:, Vorbis comments, FLV
attributes, unmapped image/XMP tags, …). Each record: { prefix, provenance, description }.
Generated from the PassthroughPrefix declarations, never hand-edited. Every such prefix is a
registered PassthroughPrefix constant; SchemaGenerator reads that registry the same way it reads
the Property table, and MetadataSchemaTest gates it identically. Adding a passthrough prefix in a
parser and forgetting to regenerate fails the build.
Not covered here: templates — parameterized key families like XMP rdf:Alt language variants
<base-key>:<lang> (dc:title:fr), where the suffix rather than the prefix is open. These are
documented by rule, not enumerated.
Classifies any key by reading the two registries above (no parser classes needed):
CLOSED (in metadata-keys.json), OPEN (under a registered
passthrough prefix), TEMPLATE (a <closed-key>:<lang> lang-alt instance), or UNKNOWN — a typo,
an unregistered namespace, or a key nobody declared. This is the payoff the registries exist for: a
data-driven legitimacy check instead of a hand-coded regex.
Together the files describe the key space of the scanned bundle: closed keys are enumerated and
gated; open namespaces are enumerated by prefix and gated;
templates are described by rule; and MetadataCoverageTest guarantees no scanned-bundle module is
silently missed. Keys from the out-of-scope families above are excluded by design.