Author a schema for your metadata standard
Your metadata standard already exists, even if it only lives in a style guide or in people’s heads: “every page needs a type,” “timestamp should be an ISO date,” “API pages must declare a version.” A JSON Schema turns those agreements into a file that docmeta can enforce on every commit. This page walks through writing that schema — from a minimal one-field example up to the fuller built-in OKF schema — and shows how a document points itself at the schema you wrote.
A schema is just a JSON file
Section titled “A schema is just a JSON file”docmeta validates against standard JSON Schema. If you can write JSON, you can write a docmeta schema — there is no docmeta-specific schema format to learn. A schema describes the frontmatter object: which keys are required, what type each value should be, and whether unexpected keys are allowed.
Here is the smallest schema worth shipping. It says: the metadata must be an object, it must have a title, and that title must be a non-empty string.
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": ["title"], "properties": { "title": { "type": "string", "minLength": 1 } }}This is the extra.schema.json fixture that docmeta’s own test suite runs, so you can trust it compiles and validates exactly as shown. Four things are doing the work:
"$schema"at the top names the JSON Schema dialect this file is written in — here, draft 2020-12. docmeta reads this line to pick the right validation engine. (This is the schema’s own dialect declaration. It is different from the$schemakey you put in a document to choose a schema — covered below.)"type": "object"says the metadata is a set of key/value pairs, which frontmatter always is."required": ["title"]is the list of fields that must be present. A document missingtitlefails validation."properties"describes individual fields.titlemust be a string, andminLength: 1rejects an emptytitle:that a contributor left blank.
A document with this frontmatter passes:
---title: Getting started---A document with empty or missing title fails. That is the whole loop: declare the shape, and docmeta holds every file to it.
Required vs. recommended fields
Section titled “Required vs. recommended fields”The single most important decision in a metadata schema is which fields are required and which are merely recommended. JSON Schema models this with one list:
- Required — list the field in
"required". Its absence is a hard failure. - Recommended (optional) — describe the field in
"properties"but leave it out of"required". If the field is present, docmeta checks its type and format; if it’s absent, that’s fine.
This distinction is what lets you encode a standard that has a strict core and a softer edge. In the example below, type is mandatory, while title and tags are validated when present but never demanded:
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "required": ["type"], "properties": { "type": { "type": "string", "minLength": 1 }, "title": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string" } } }}Constrain values with types and formats
Section titled “Constrain values with types and formats”Requiring a field guarantees it exists; constraining it guarantees it’s usable. JSON Schema gives you the building blocks, and docmeta supports the standard format checks through ajv-formats. Two formats come up constantly in document metadata:
Use "format": "date-time" to require an ISO 8601 timestamp. This catches the classic fat-fingered date — 2026-13-01, June 5th, or a bare 2026-06-25 where a full timestamp was expected.
"timestamp": { "type": "string", "format": "date-time", "description": "ISO 8601 datetime of last meaningful change."}Passes: 2026-06-25T14:30:00Z
Fails: June 5th, 2026-13-01
Use "format": "uri" for fields that must hold a well-formed URI, such as a canonical resource link. This checks the shape of the URI; it does not fetch the URL.
"resource": { "type": "string", "format": "uri", "description": "A URI uniquely identifying the underlying asset."}Passes: https://example.com/asset/42
Fails: not a url, example.com (no scheme)
Beyond formats, the everyday constraints carry most of the weight:
enum— restrict a field to a fixed set of values, ideal for a controlledtypeorstatus:"enum": ["guide", "reference", "tutorial"].minLength— reject empty strings ("minLength": 1).array+items— validate every element of a list, as with thetagsexample above.
Decide whether unknown fields are allowed
Section titled “Decide whether unknown fields are allowed”By default, JSON Schema lets a document carry fields your schema never mentions. Whether to permit that is a deliberate choice, controlled by additionalProperties:
"additionalProperties": true(or omitting it) — extra keys are tolerated. Contributors can add experimental or tool-specific frontmatter without tripping the check. This is the lenient default and a good fit for a young or shared standard."additionalProperties": false— every key must be declared in"properties". An unexpected key is a failure. Use this to catch typos liketitel:or to keep frontmatter tightly governed.
A fuller example: the built-in OKF schema
Section titled “A fuller example: the built-in OKF schema”docmeta ships with one built-in schema, Open Knowledge Format (OKF) v0.1, addressed as google:okf:0.1. It’s a useful reference because it combines every technique on this page in a realistic standard. Reading it is the fastest way to see how the pieces fit together — and it’s the schema docmeta falls back to when no other schema applies.
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "google:okf:0.1", "title": "Open Knowledge Format (OKF) v0.1", "description": "Frontmatter schema for OKF v0.1 concept files. OKF requires only `type`; all other fields are recommended, and unknown keys are explicitly tolerated.", "type": "object", "required": ["type"], "additionalProperties": true, "properties": { "type": { "type": "string", "minLength": 1, "description": "A short string identifying the kind of concept." }, "title": { "type": "string", "description": "Human-readable display name." }, "description": { "type": "string", "description": "Single-sentence summary used for indexing and previews." }, "resource": { "type": "string", "format": "uri", "description": "A URI uniquely identifying the underlying asset." }, "tags": { "type": "array", "items": { "type": "string" }, "description": "Short strings for categorization." }, "timestamp": { "type": "string", "format": "date-time", "description": "ISO 8601 datetime of last meaningful change." } }}Notice the design choices OKF makes, each one a decision you’ll face in your own schema:
- Exactly one required field (
type). OKF demands the bare minimum, so adopting it rarely breaks existing files. additionalProperties: true, stated explicitly. Unknown keys are welcome by design — OKF is meant to be extended.- Recommended fields with real constraints.
title,description, andtagsare optional, but if present they’re checked.resourceandtimestampcarryurianddate-timeformats. - A
$idand human-readabletitle/description. These aren’t required for validation, but they document the schema for the humans who read it — and$idis what gives the built-in itsgoogle:okf:0.1address. Setting a$idin a schema file of your own is documentation only; it does not register a new built-in id (built-ins are bundled into docmeta). You’d reference your own schema by its file path or URL, not by its$id.
Point a document at its schema
Section titled “Point a document at its schema”A schema only matters once documents are validated against it. The most direct way to connect the two is to let a document name its own schema with a $schema key in its frontmatter. docmeta reads that key and validates the file against whatever it points to.
Here is a document naming the built-in OKF schema — the schema-ref.md fixture:
---$schema: google:okf:0.1type: guidetitle: Self-Describing Document---
# Self-Describing Document
This file names its own schema via `$schema`.The $schema value can be a built-in id (as here), a path to a .json file you wrote, or a URL. To point at a local schema file instead:
---$schema: ./schemas/metadata.schema.jsontype: guidetitle: My page----
Write your schema as a
.jsonfile using the patterns above, keeping the"$schema"dialect line at the top. -
Add
$schema:to a document’s frontmatter, pointing at your file (or a built-in id, or a URL). -
Validate it:
Terminal window npx docmeta validate path/to/document.mddocmeta resolves the document’s
$schema, validates the frontmatter, and reports any missing or malformed fields.