Three fields, three different sentences
Every document's metadata may carry up to three schema-related fields. They are declared on the base document schema, hypermedia-metadata, and each one says a different thing:
field | the sentence it says | value |
|---|---|---|
| "This document conforms to that type." | an |
| "My children must conform to that type." | an |
| "This document defines a type others can reference." |
|
The one that trips people up is the last. schemaDefinition does not mean "this document follows a schema." It means "this document is the home page of a schema." A document that describes what a person is sets schemaDefinition. A document about a particular person — Bob — sets schema, pointing at the person document. A value is never a type.
A worked example
Suppose the Acme account wants person pages.
Acme publishes a schema blob. It extends the base document, hypermedia-document, and requires a surname in metadata. The blob has a CID.
Acme publishes a document at hm://acme/person — a readable page explaining what a person page is — with schemaDefinition = ipfs://<that cid>. This is now the person type, addressable by name.
Acme publishes hm://acme/people/bob with schema = hm://acme/person. The app fetches the person document, follows its schemaDefinition to the blob, and now knows Bob's page must carry a surname.
Acme sets childrenSchema = hm://acme/person on hm://acme/people. Every child created under it is a person page by default; nobody has to remember to set schema on each one.
The library ships this exact shape as an example: example-person-doc is a typed document schema that refines metadata, and example-bob is a live instance document whose schema points at its type.
The effective schema
A document's effective conformance schema is decided by one rule: its own schema if it has one, otherwise its parent's childrenSchema, otherwise none. A child that declares its own schema while its parent declares a childrenSchema is expected to satisfy both — and, like every typed document, to descend from the base document.
This is what makes a directory typed without making every page repeat itself, and what lets one page opt out (or into something more specific) explicitly.
Extending the base document
A typed document schema is an ordinary Onyx extension. It references the base document and refines the nested metadata — adding properties, marking some required — and may constrain content too. Here is the shape of the person-document schema from the library, in dag-json:
{
"ref": "hm://z6MkmZUb…/hypermedia-document",
"properties": {
"metadata": {
"ref": "hm://z6MkmZUb…/hypermedia-metadata",
"required": ["surname"],
"properties": {
"surname": {"ref": "hm://z6MkmZUb…/string"},
"givenName": {"ref": "hm://z6MkmZUb…/string"}
}
}
}
}Because the base is hypermedia-document — { metadata, content } where content is the block tree — a typed document is still a full document with a body, embeds, queries, and comments. Typing adds structure to a page; it never takes the page away. Extension semantics are described in the schema language.
What the editor does with it
Once a document has an effective schema, the Seed app changes in four visible ways:
Required attributes are always present. Each required field from the resolved schema is a fixed, non-removable row — at the top of the Attributes tab and above the body in the Content tab — so a person page can never quietly lose its surname.
Fields get the right control. A field whose format is a Hypermedia URL renders as a searchable, clickable title pill rather than a raw string. A field whose format is an IPFS reference gets a file picker and a file pill. Enums become dropdowns. schema and childrenSchema are themselves document-reference fields; icon, cover, and schemaDefinition are IPFS-reference fields.
Problems are shown in red and never block. A per-field badge and a summary banner list the actual violations — "surname is required", "status must be one of draft, published, archived". Saving always works. Validation is a guardrail, not a gate; see why Onyx.
A type's home page gets actions. A document carrying schemaDefinition shows a header tag that opens the schema in the explorer and a Create button that opens a value editor for that type and publishes a new conforming document — one whose schema is this page's URL.
Dates, references, and linked objects
Three kinds of field make a typed document feel like a record rather than a bag of text:
Dates. date and date-time are built-in refinements of string — an ISO 8601 YYYY-MM-DD calendar date and an RFC 3339 instant — with a pattern so a validator can check the shape. In the editor a date field is a date picker; the value on the wire is still the plain string.
References with a target. A field whose format is hm-url or ipfs may carry a target: the schema the referenced document or object is expected to conform to. character.home targets the Place type; character.stats targets Character stats. A target is advisory — the validator never dereferences a reference — but the editor uses it to pre-seed and validate what you create.
Linked objects. An ipfs field can point at a file (uploaded) or at an object — a DAG-CBOR value authored right in the Attributes editor. Press Create object on an empty field: with a target, the editor is locked to that type and publishes only a conforming value; without one, pick any schema (advisory) or choose free-form data. The published object carries a schema link to its type, the field is set to ipfs://<cid>, and the pill offers to open or edit it. Editing publishes a new version and re-points the field, because blobs are immutable.
Doing it yourself
With Developer Mode on, from any document's options menu:
New Schema opens the schema editor. Build the type — or start from the base document to make a typed document schema. Publishing mints the blob and gives you an ipfs:// CID.
On the page that should be the type's home, set schemaDefinition to that CID in the Attributes editor. The page now shows the schema tag and the Create button.
On a page that should be an instance, set schema to the home page's hm:// URL — or press Create on the type's page. Required fields appear immediately.
On a folder, set childrenSchema to the same URL to type everything beneath it.
Pinning versus following
A reference by CID pins exact bytes: the type can never change under you, and you must republish to adopt a newer one. A reference by hm:// URL follows the type's document, which the owner may update — new fields appear on every instance the next time it is opened. Both are legitimate; the library uses names so that schemas can reference each other in cycles and so that a type can evolve in place. The choice should be deliberate: pin when you need a stable contract, follow when you want the type's owner to be able to improve it. The versioning trade-off is discussed further in references & naming.
Do you like what you are reading? Subscribe to receive updates.
Unsubscribe anytime