A schema is only useful once manni meta knows which schema applies to which file. In a real repo, several signals compete to answer that question. A flag on the command line and a $schema key inside the document both have a claim. So do a directory rule in your config and a repo-wide default. manni meta 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 and the three kinds of schema reference you can use anywhere a schema is named. It also covers how manni meta detects a schema’s JSON Schema dialect.
For every file, manni meta 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.
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.
The file’s own $schema key: a $schema field in the document’s metadata. May be a single string or a list of strings.
The first matching overrides entry in config: config overrides are checked in order; the first whose glob matches the file path supplies the set.
Config schemas: the repo-wide default list in manni.config.yaml.
The built-in default set: if nothing above matched, manni meta uses google:okf:0.1 and passo-uno:seven-action:1.0 together.
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. A 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 set
All files (last resort)
google:okf:0.1 + passo-uno:seven-action:1.0.
Levels 3 and 4 come from your manni.config.yaml. Here is the config the worked example below assumes, with a repo-wide default plus one directory override:
manni.config.yaml
meta:
# 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 schemas list with the files it governs. The
files are named in one of two ways. A files glob, as above, names them by
pattern. collection: <name> names the members of a declared
collection.
An entry carries exactly one of the two. Entries are tried in order, and the
first that matches the file supplies the set, whichever way it names its files.
Reach for collection: when the set is a real document set the repository
already declares, so the globs are written once. Keep files: for a glob that
only affects which schema applies. (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 manni meta, not part of the document’s metadata. manni meta 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 manni meta 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:
The file is validated against both schemas; see below.
Level 2 beating config is what makes a document self-describing, and it is also the one level a repository can narrow. A repo taking pull requests from outside can narrow it with schemaTrust. It can say that documents may name a built-in id or a repository file but not a URL. It can also ignore their $schema entirely. Left unset, level 2 behaves exactly as described here.
Everywhere a schema is named, the value is a reference. That covers a --schema flag, a $schema key, and a config entry. manni meta classifies each reference into one of three kinds by its shape. You don’t declare the kind; manni meta 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 manni meta. 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 .jsonor contains a path separator (/ or \).
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. The same URL is downloaded only once, even across thousands of files.
URL references are one way to govern a single schema across many repos: a central, versioned schema URL that every consumer points at. Vendoring that URL into each consumer is the other, and the more durable one. Both, with the fetch timeout and caching behavior in depth, are covered in Govern a shared schema across repos.
When a file’s schema set contains more than one reference, manni meta validates the file against every schema in the set. It 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.jsonand 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 manni meta reads each schema’s own $schema line and selects the matching validation engine. That line is the URI at the top of the schema file. It points at the dialect’s meta-schema, the schema that defines the dialect itself.
manni meta 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, manni meta falls back to 2020-12, the dialect of the built-in schemas. Because of that auto-detection, a schema you fetch from a remote URL can target draft-07. It still 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.