Skip to content

How schema resolution works & how to wire it

A schema is only useful once docmeta knows which schema applies to which file. In a real repo, several signals compete to answer that question: a flag on the command line, a $schema key inside the document, a directory rule in your config, a repo-wide default. docmeta settles the competition with a fixed precedence order — and once you know that order, wiring schemas to documents becomes predictable instead of mysterious.

This page documents the precedence chain, the three kinds of schema reference you can use anywhere a schema is named, and how docmeta detects a schema’s JSON Schema dialect.

For every file, docmeta builds a schema set — the list of schemas that file will be validated against — by checking five sources in order. The first source that yields a schema wins, and the search stops there. Nothing lower in the list is consulted.

  1. CLI -s/--schema — if you pass one or more --schema flags, they apply to every file in the run and override everything else. Repeatable; the flags together form the set.

  2. The file’s own $schema key — a $schema field in the document’s metadata. May be a single string or a list of strings.

  3. The first matching overrides entry in config — config overrides are checked in order; the first whose glob matches the file path supplies the set.

  4. Config schemas — the repo-wide default list in docmeta.config.yaml.

  5. The built-in default — if nothing above matched, docmeta uses google:okf:0.1.

Here is the same chain as a quick-reference table:

Order Source Scope Notes
1 CLI -s/--schema All files in the run Repeatable; overrides everything below.
2 File $schema key That one file String or list of strings. Reserved key — stripped before validation.
3 Config overrides Files matching a glob First matching entry wins; later entries ignored.
4 Config schemas All files (repo default) Used when no override matched.
5 Built-in default All files (last resort) google:okf:0.1.

Levels 3 and 4 come from your docmeta.config.yaml. Here is the config the worked example below assumes — a repo-wide default plus one directory override:

docmeta.config.yaml
# Level 4: the repo-wide default applied to every file...
schemas:
- google:okf:0.1
# Level 3: ...unless a file matches an override glob, checked top to bottom.
overrides:
- files: "api/**"
schemas:
- ./schemas/api.schema.json

Each overrides entry pairs a files glob with a schemas list. Entries are tried in order, and the first whose glob matches the file supplies the set. (For every config key, type, and default, see the Configuration reference.)

When a document names its own schema with a $schema key, that key is a directive to docmeta, not part of the document’s metadata. docmeta strips $schema out of the metadata before handing it to the validator.

This matters for one specific case: a schema with "additionalProperties": false rejects any field it doesn’t declare. Without special handling, a document carrying $schema: would fail against such a schema — because $schema isn’t one of the schema’s declared properties. Because docmeta removes $schema first, that never happens. You never need to add $schema to your schema’s properties, and a self-describing document validates cleanly against even the strictest schema.

The $schema value can be a single reference or a list:

---
$schema: ./schemas/metadata.schema.json
type: guide
title: My page
---

Everywhere a schema is named — a --schema flag, a $schema key, a config entry — the value is a reference, and docmeta classifies it into one of three kinds by its shape. You don’t declare the kind; docmeta infers it.

Built-in

A vendor:name:version id with no path separators and not ending in .json.

Example: google:okf:0.1

Resolved from the schemas bundled into docmeta. A typo’d id (e.g. an unknown vendor) is reported as an unknown built-in, not silently treated as a missing file.

File

A path that ends in .json or contains a path separator (/ or \).

Examples: ./schemas/api.schema.json, schemas/house.json

Read from the local filesystem. Relative paths resolve against the current working directory.

URL

Anything starting with http:// or https://.

Example: https://example.com/schemas/okf-1.0.json

Fetched over the network with a 10-second timeout and cached for the run, so the same URL is downloaded only once even across thousands of files.

URL references are the foundation for governing one schema across many repos — a central, versioned schema URL that every consumer points at. That workflow, including the fetch timeout and caching behavior in depth, is covered in Govern a shared schema by URL.

When a file’s schema set contains more than one reference, docmeta validates the file against every schema in the set and reports the union of all violations, each tagged with the schema that produced it. A file passes only if it satisfies all of them.

A multi-schema set is what you get when you name several schemas at a single precedence level:

  • several --schema flags on the command line,
  • a list-valued $schema key in a document,
  • or multiple entries in one config overrides rule or in config schemas.

This is useful for layering a shared organizational schema with a local one — for example, validate against a company-wide house-style.schema.json and a team-specific api.schema.json at once. Each schema is compiled and cached separately, so layering is cheap.

Because the file must satisfy every schema in the set, the strictest member governs. If any schema in the set sets "additionalProperties": false, an unknown key fails the file even when another schema in the set allows it. Keep that in mind when combining a lenient shared schema with a strict local one — the strict one wins on every rule it imposes.

JSON Schema has evolved through several versions, called dialects (draft-04, draft-06, draft-07, 2019-09, 2020-12). A schema written for one dialect can fail to compile under another, so docmeta reads each schema’s own $schema line — the URI at the top of the schema file that points at the dialect’s meta-schema (the schema that defines the dialect itself) — and selects the matching validation engine.

docmeta supports these dialects:

Dialect Detected from a $schema URI containing
2020-12 2020-12 (also the fallback)
2019-09 2019-09
draft-07 draft-07 or draft/7
draft-06 draft-06 or draft/6 (shares the draft-07 engine)
draft-04 draft-04 or draft/4

If a schema omits its $schema line or uses an unrecognized one, docmeta falls back to 2020-12 — the dialect of the built-in schemas. This auto-detection is why a schema you fetch from a remote URL that targets draft-07 validates correctly alongside a local 2020-12 schema in the same run.

For the full lookup table of precedence, reference kinds, and dialects in one place, see the Schema resolution reference.