The Onyx data model
Onyx types values drawn from the IPLD data model — the same set of kinds DAG-CBOR can encode. There are nine kinds. Every value is exactly one of them; there is nothing else.
kind | JSON / dag-json form | notes |
|---|---|---|
|
| |
|
| |
|
| DAG-CBOR encodes ints and floats differently |
|
| |
|
| UTF-8 text |
|
| raw octets; base64 in dag-json |
|
| ordered sequence |
|
| keys are strings; ordered, unique |
|
| a CID — a content-addressed pointer to another block |
Why these are all built-in
A recurring question when adopting IPLD: are link and bytes special types we define in the schema language, or primitives? Primitives. And this is not a new decision — it is the same status string and integer already have.
Nothing in Onyx defines what a string is; the codec does. Onyx only names the kind so a schema can constrain a field to it. link and bytes are identical in standing: the codec (DAG-CBOR) owns their existence and their wire form, and Onyx simply names them in the type vocabulary. The schema language has always been a set of names for codec-defined kinds. Two more names changes nothing structural.
The practical consequence, spelled out in the schema language and encoding: never model the {"/":…} representation as a map inside a schema. A link is not "a map with a / key" — it is its own kind that merely renders that way in JSON. Treat it as atomic and opaque, exactly like a string.
integer vs float
JSON has one number type; DAG-CBOR has two, encoded with different major types. If you collapse them into one Onyx kind you lose round-trip fidelity: a value authored as 1.0 might re-encode as the integer 1. So Onyx keeps them distinct.
The seam is JavaScript/JSON, which cannot tell 3.0 from 3. The reference validator therefore treats integer strictly (Number.isInteger) and float permissively (any number). A real DAG-CBOR pipeline preserves the distinction in the bytes, where it is unambiguous.
map vs struct — one kind, two constraints
At the data-model level there is only map. There is no separate "object" or "struct" kind. "Struct" is a schema-level idea: a map whose keys are known in advance. Onyx expresses both shapes over the single map kind:
known, named fields → constrain with properties (struct-like)
arbitrary keys, uniform values → constrain with values (open map)
See the schema language. This is why the vocabulary has no object type: the kind is map, and how you constrain it is a separate axis.
link is the whole point
A link is a CID: a hash that names another block by its content. Links are what make Onyx data a DAG (directed acyclic graph) spanning many blocks rather than one document. A schema field typed link says "here is a pointer to another block," and — optionally — "whose value should itself match schema X" (a typed link; see references). See example-document (author links to a person, previous to another document) and the mutually-linked example-folder / example-file.
Onyx uses this same machinery on itself: schemas link to other schemas, so the type definitions form their own DAG, addressed and resolved exactly like the data they describe.
The primitive schemas — onyx-<kind>
A kind like string is a name in the vocabulary; {"type":"string"} is the schema for a string value. Onyx ships that schema as a canonical, named block — one per kind:
primitive | is exactly | typed by |
|---|---|---|
|
|
|
|
|
|
|
|
|
These are the standard library. Two layers, not to be confused:
onyx-scalar-schema (a meta-schema variant) describes the shape {type:<scalar>, enum?} — it is the type of onyx-string.
onyx-string (a primitive) is {"type":"string"} — an instance of that shape, and the block you actually reference.
Instead of inlining {"type":"string"} in every schema, reference the primitive: { "ref": "onyx-string" }. On IPFS that ref becomes the CID of the onyx-string block, so a field's type is itself a content-addressed link — the same mechanism as any other reference (references). The example schemas do exactly this; open example-person and every field is a ref to a primitive or another schema.
Do you like what you are reading? Subscribe to receive updates.
Unsubscribe anytime