profiles/v0.umsg
%unimsg 0 profile/v0 · 50 lines · 2248 bytes of text · 1071 bytes canonical · the raw file
Point at anything below — a sigil, a literal, a brace — and the card says what it is. Keyboard: tab through the document. Every explanation is also printed in full beneath the source.
%unimsg 0 profile/v0
-- The v0 conformance profile, expressed in unimsg rather than in prose.
--
-- Restrictions used to live as English in docs/12, which meant the only way
-- to enforce one was to hardcode a check. As a document a profile is
-- content-addressed, versionable, composable, and checkable by generic code.
profile @unimsg/v0 {
doc "Conformance profile for unimsg v0."
-- D10/F6: native float equality diverges from byte equality. -0.0 and 0.0
-- are byte-distinct but compare equal; NaN and NaN are byte-identical but
-- compare unequal. Banning float keys removes the divergence class rather
-- than asking every implementer to handle it.
--
-- Bytes joined float on the same argument, having been allowed here for
-- longer than anything implemented it. A byte-string key is unhashable in
-- some hosts and indistinguishable from a text key in others, which is the
-- same divergence wearing different clothes. Nothing in the repository ever
-- wrote one; six implementations refused them in six different ways, and
-- the profile said they were fine.
keys {
allow [ :text, :int ]
reject [ :float, :bytes ]
reason "F6 — native float equality diverges from byte equality, and a bytes key diverges from a text key the same way"
}
-- Encoding-level rules. These cannot be checked from a decoded value; the
-- validator needs the bytes.
encoding {
determinism :required
duplicate-keys :reject
}
limits {
doc "WHAT max-depth COUNTS, settled 2026-09-02: containers of the value
model. The document body is depth 0 and each map or sequence entered
adds one, so a table row's cells are two below the table's parent —
the sequence, then the row's map. Tags, annotations, qualifiers,
decimals, references and extensions add nothing, because they are how
a scalar is written and not what an author nests. This is the count
`umsg profile` applies. A decoder's stack-safety cap is a different
number, measured on the encoding, where a decimal is a tag around a
two-element array; it must sit comfortably above this one, and the
implementer's guide says how far."
max-depth 500
}
}
The canonical encoding
Produced by the reference encoder when this page was built. Decoding these bytes and printing them in the text form returns the document above, byte for byte — that round trip is the claim the whole format rests on.
a1 67 70 72 6f 66 69 6c 65 da 00 01 11 d5 82 81 da 00 01 11 d6 69 75 6e
69 6d 73 67 2f 76 30 a4 63 64 6f 63 78 22 43 6f 6e 66 6f 72 6d 61 6e 63
65 20 70 72 6f 66 69 6c 65 20 66 6f 72 20 75 6e 69 6d 73 67 20 76 30 2e
64 6b 65 79 73 a3 65 61 6c 6c 6f 77 82 da 00 01 11 d4 64 74 65 78 74 da
00 01 11 d4 63 69 6e 74 66 72 65 61 73 6f 6e 78 6f 46 36 20 e2 80 94 20
6e 61 74 69 76 65 20 66 6c 6f 61 74 20 65 71 75 61 6c 69 74 79 20 64 69
76 65 72 67 65 73 20 66 72 6f 6d 20 62 79 74 65 20 65 71 75 61 6c 69 74
79 2c 20 61 6e 64 20 61 20 62 79 74 65 73 20 6b 65 79 20 64 69 76 65 72
67 65 73 20 66 72 6f 6d 20 61 20 74 65 78 74 20 6b 65 79 20 74 68 65 20
73 61 6d 65 20 77 61 79 66 72 65 6a 65 63 74 82 da 00 01 11 d4 65 66 6c
6f 61 74 da 00 01 11 d4 65 62 79 74 65 73 66 6c 69 6d 69 74 73 a2 63 64
6f 63 79 02 d6 57 48 41 54 20 6d 61 78 2d 64 65 70 74 68 20 43 4f 55 4e
54 53 2c 20 73 65 74 74 6c 65 64 20 32 30 32 36 2d 30 39 2d 30 32 3a 20
63 6f 6e 74 61 69 6e 65 72 73 20 6f 66 20 74 68 65 20 76 61 6c 75 65 0a
20 20 20 20 20 20 20 20 20 6d 6f 64 65 6c 2e 20 54 68 65 20 64 6f 63 75
6d 65 6e 74 20 62 6f 64 79 20 69 73 20 64 65 70 74 68 20 30 20 61 6e 64
20 65 61 63 68 20 6d 61 70 20 6f 72 20 73 65 71 75 65 6e 63 65 20 65 6e
74 65 72 65 64 0a 20 20 20 20 20 20 20 20 20 61 64 64 73 20 6f 6e 65 2c
20 73 6f 20 61 20 74 61 62 6c 65 20 72 6f 77 27 73 20 63 65 6c 6c 73 20
61 72 65 20 74 77 6f 20 62 65 6c 6f 77 20 74 68 65 20 74 61 62 6c 65 27
73 20 70 61 72 65 6e 74 20 e2 80 94 0a 20 20 20 20 20 20 20 20 20 74 68
65 20 73 65 71 75 65 6e 63 65 2c 20 74 68 65 6e 20 74 68 65 20 72 6f 77
27 73 20 6d 61 70 2e 20 54 61 67 73 2c 20 61 6e 6e 6f 74 61 74 69 6f 6e
73 2c 20 71 75 61 6c 69 66 69 65 72 73 2c 0a 20 20 20 20 20 20 20 20 20
64 65 63 69 6d 61 6c 73 2c 20 72 65 66 65 72 65 6e 63 65 73 20 61 6e 64
20 65 78 74 65 6e 73 69 6f 6e 73 20 61 64 64 20 6e 6f 74 68 69 6e 67 2c
20 62 65 63 61 75 73 65 20 74 68 65 79 20 61 72 65 20 68 6f 77 0a 20 20
20 20 20 20 20 20 20 61 20 73 63 61 6c 61 72 20 69 73 20 77 72 69 74 74
65 6e 20 61 6e 64 20 6e 6f 74 20 77 68 61 74 20 61 6e 20 61 75 74 68 6f
72 20 6e 65 73 74 73 2e 20 54 68 69 73 20 69 73 20 74 68 65 20 63 6f 75
6e 74 0a 20 20 20 20 20 20 20 20 20 60 75 6d 73 67 20 70 72 6f 66 69 6c
65 60 20 61 70 70 6c 69 65 73 2e 20 41 20 64 65 63 6f 64 65 72 27 73 20
73 74 61 63 6b 2d 73 61 66 65 74 79 20 63 61 70 20 69 73 20 61 20 64 69
66 66 65 72 65 6e 74 0a 20 20 20 20 20 20 20 20 20 6e 75 6d 62 65 72 2c
20 6d 65 61 73 75 72 65 64 20 6f 6e 20 74 68 65 20 65 6e 63 6f 64 69 6e
67 2c 20 77 68 65 72 65 20 61 20 64 65 63 69 6d 61 6c 20 69 73 20 61 20
74 61 67 20 61 72 6f 75 6e 64 20 61 0a 20 20 20 20 20 20 20 20 20 74 77
6f 2d 65 6c 65 6d 65 6e 74 20 61 72 72 61 79 3b 20 69 74 20 6d 75 73 74
20 73 69 74 20 63 6f 6d 66 6f 72 74 61 62 6c 79 20 61 62 6f 76 65 20 74
68 69 73 20 6f 6e 65 2c 20 61 6e 64 20 74 68 65 0a 20 20 20 20 20 20 20
20 20 69 6d 70 6c 65 6d 65 6e 74 65 72 27 73 20 67 75 69 64 65 20 73 61
79 73 20 68 6f 77 20 66 61 72 2e 69 6d 61 78 2d 64 65 70 74 68 19 01 f4
68 65 6e 63 6f 64 69 6e 67 a2 6b 64 65 74 65 72 6d 69 6e 69 73 6d da 00
01 11 d4 68 72 65 71 75 69 72 65 64 6e 64 75 70 6c 69 63 61 74 65 2d 6b
65 79 73 da 00 01 11 d4 66 72 65 6a 65 63 74
profiles/v0.umsg 1071 bytesEvery construct on this page
The formal columns are read from the specification when this page is generated. The last column is this site's plain-language gloss and is not normative — where they differ, the specification is right.
| construct | glyph | carries | example | gloss |
|---|---|---|---|---|
| :annotation | — | — | — | A bare name qualifying the value after it—for example a language tag or a unit such as kg. It is not a value itself; a bare name with nothing after it is an error, not a guess. |
| :array | ^ | type, or type:shape; then [ elements ] | ^f64 [ 1.5, 2.5 ] or ^i16:2x3 [ … ] | A packed, homogeneous numeric array. The ^ marker gives every element its type and may also give the row-major shape: ^f64:2x3 [ … ]. |
| :array-element | — | — | — | One element of a typed array. Its type comes from the ^ marker, so 1.5 inside ^f64 is binary64 rather than an exact decimal. |
| :array-missing | — | — | — | A missing position in a typed array. null here is not an element value: it adds a validity mask while preserving the array's shape. |
| :block | — | — | — | A block: named pairs, in braces. Its keys are sorted in the encoding and shown here as written. |
| :bytes | ~ | encoding:data | ~hex:ff00 or ~b64:iVBORw0KGgo= | Raw octets, carried as octets. No base64 tax, no pretending they are text. |
| :comment | -- | to end of line | -- a note | A note for people. It travels with the pair it precedes and never reaches the bytes. |
| :decimal | — | — | — | An exact decimal. 19.99 is nineteen and ninety-nine hundredths, not the nearest double to it. |
| :extension | ! | name, then value | !acme/hint { zone 4 } | A qualifier this reader may not know. It survives the round trip intact, and stays legible on the way past — the rule that separates an extension from a private convention. |
| :float | — | — | — | A binary float, marked so it can never be mistaken for the exact decimal beside it. |
| :hash | # | algorithm:hex | #sha256:deadbeef | The digest of some content, with the algorithm said out loud so it is still meaningful in ten years. |
| :header | % | unimsg only | %unimsg 0 label | Declares the format version and what this document is. Optional, and useful exactly when a file arrives without context. |
| :identifier | @ | word | @ord-88213 | Gives this value a name, so another document can point at it without copying it. |
| :integer | — | — | — | A whole number of any size. Past 2^53 it is still exact, which is the whole reason this is not JSON. |
| :key | — | — | — | A field name: the first thing in a pair. It is bare when possible and quoted when needed, and names the content without becoming part of the value. |
| :reference | -> | an identifier or a path | -> @cust-4471, -> @lexicon.entries.kitāb | Points at a name. Nothing follows it while reading — resolution is somebody else's job, deliberately. |
| :reserved-literal | — | — | — | One of six bare words that are values rather than names. The list is fixed, so no value is coerced by what it looks like. |
| :sequence | — | — | — | An ordered list. Order is preserved and is meaning. |
| :string | — | — | — | Text. May span lines, so prose does not have to be escaped into one. |
| :symbol | : | word | :dispatched | A name from a fixed vocabulary, not free text. :high is a value someone chose; "high" is a phrase someone typed. |
| :table-row | | | cells | | sku qty | One record of a table. A table is sugar for a sequence of blocks with the same keys, and encodes identically to the longhand. |
| — | — | — | A temporal value at the precision written. 2026-08 stays a month, and 03:52 stays a wall-clock time; neither is widened into an invented instant. |