Components
Fields give durable names to knowledge, but a name does not determine what kind of value carries its meaning. JSON covers common values across languages, yet it cannot distinguish text from a timestamp or a number from an entity reference. Stardust keeps those distinctions as typed values without making one programming language authoritative. Each field holds one typed value inside a shared JSON-compatible model.
Meaning must be explicit
A field name or the appearance of a string does not decide a value's type. A timestamp-looking string remains text unless the value is written as a tagged UTC instant. This rule prevents conventions in one application from changing data written by another. Fields and value types therefore remain independent concerns.
JSON numbers need a similarly exact boundary because JSON does not declare integer widths or floating-point intent. A number without a decimal point or exponent is stored as a signed 64-bit integer. A number with a decimal point or exponent is stored as a 64-bit floating-point number. An integer literal too large for 64 bits is stored as a floating-point number. Infinity and not-a-number values have no JSON form and are not accepted.
Type participates in stored equality. A write compares the encoded value, including its type, with the current fact. Replacing integer 1 with floating-point 1.0 or text "1" records a new fact. Queries are more forgiving for numbers: comparison, ordering, and joins treat 2 and 2.0 as equal, while text "2" stays distinct.
A small typed vocabulary
A single-key object whose key starts with # gives a type an explicit JSON-compatible form. The same value passes through JSON, DUST, the mount, and different programming languages without relying on a native class. A small official vocabulary covers meanings that projects frequently use, one per field here:
reference {#link 42}
instant {#utc 2024-01-02T03:04:05Z}
duration {#dur PT1H30M}
uuid {#uuid 0123456789abcdef0123456789abcdef}
digest {#sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855}
| Field | Type | Reason for a distinct type |
|---|---|---|
reference | Entity reference | The reference creates a relationship with another entity. |
instant | UTC instant | The value identifies one global instant. |
duration | Duration | The value measures a fixed amount of elapsed time. |
uuid | UUID | The value identifies data independently of a Stardust entity. |
digest | SHA-256 digest | The value identifies exact content. |
A reference names its target by id, the only address a data entity has. A path spelling such as {#link users/alice} is not a reference and is refused when the value is written. Queries also accept a bare # key as a short form of #link and treat both spellings as the same entity.
A UTC value ends in Z. Input can stop after minutes or include seconds and up to seven fractional-second digits. Stardust writes seconds in its output.
The duration form uses days, hours, minutes, and seconds, as in P1DT2H or PT90S. It excludes calendar-dependent months, years, and weeks because their lengths depend on context. Stardust stores UTC instants and durations as signed counts of 100-nanosecond ticks. A UUID is written as exactly 32 hexadecimal digits without hyphens, and a SHA-256 digest as exactly 64.
A SHA-256 value identifies exact content. Binary objects use the same type: each file under objects/ is an entity whose stardust/object/digest field holds the SHA-256 digest of its stored payload. A SHA-256 value in an ordinary field is independent of binary storage.
These official types solve recurring interoperability problems rather than defining every domain. Their explicit forms support validation and exact round-trip conversion while remaining ordinary JSON-compatible objects. An untagged string never becomes one of these types because it has a similar format. Any other # key is reserved and rejected on write.
Domains can extend the vocabulary
Projects need values that a general data engine should not own, such as money, measurements, or external protocol identifiers. A custom tagged value names its tag and carries a payload:
tagged {#tag color
value red}
The value has exactly two members, #tag and value. The tag is any non-empty string, and the payload is any value. Stardust does not require a naming scheme, so a reverse-DNS style such as com.example/money is a project convention for avoiding collisions rather than a rule.
A custom tag keeps its payload as one typed value. The tag declares what the owner says the value means, but Stardust does not supply the domain interpretation. Schemas can validate the payload structure where a workflow needs it. Applications remain responsible for rules that need domain knowledge.
Structure inside a value
Arrays and untagged objects are also values. A field that holds {deeper {flag false}} or [alpha beta] keeps the whole structure as one fact. A patch that sets such a field replaces the whole value rather than merging into it. Object keys inside a stored value cannot start with #, so a tagged object is never confused with an ordinary one.
The distinction also keeps the ontology open. One field can hold different value types on different entities until a selected schema requires a narrower rule. A schema adds agreement where a workflow needs it.