Appendix — Canonical AEON Form
Appendix to: AEON Specification v1
Overview
Canonical AEON defines a deterministic textual representation suitable for hashing, diffing, signing, and reproducible builds.
Canonical form is optional for parsing but REQUIRED for canonical emitters.
Document Structure
Header
If no header exists, emit the default:
aeon:header = {
encoding = "utf-8"
mode = "transport"
profile = "core"
version = "1.0"
}
Header fields sorted lexicographically.
Top-Level Order
aeon:headerfirstAll other bindings in lexicographic order
This ordering is not formatter-only advice:
a structured header appearing after any body binding is invalid input;
canonicalization MUST reject such input rather than reordering it into a valid document.
Keys and Objects
Object keys sorted lexicographically
Attribute keys sorted lexicographically
One key per line in multi-line objects
Canonical layout is structure-sensitive:
inline object forms may remain inline when the enclosing canonical layout is inline;
when an enclosing object or list is rendered as multiline canonical layout, nested object values also canonically expand to multiline object blocks rather than remaining inline;
key sorting still applies at every level regardless of whether the object is rendered inline or multiline.
Lists
Element order preserved (never reordered)
Single-line for simple scalars, multi-line for complex values
When a list is rendered multiline, complex object elements canonically expand to multiline object blocks.
Scalars
Numbers
Canonicalization of
:nvalues is value-normalizing while preserving the broad representation family chosen by the author:integer family
decimal family
exponent family
Remove all
_separatorsNo leading
+signLeading-dot decimals gain an explicit zero (
.5→0.5)Decimal-family values trim redundant trailing fractional zeroes, but retain at least one fractional digit (
10.00→10.0)Exponent-family values use lowercase
eExponent-family values remove redundant exponent sign and leading exponent zeroes (
1.0E+03→1e3)Zero follows the same family model rather than a special ad hoc rule:
integer zero →
0or-0decimal zero →
0.0or-0.0exponent zero →
0e0or-0e0
// Non-canonical → Canonical
1_000_000 → 1000000
1.2300 → 1.23
.10 → 0.10
.5 → 0.5
1.0E+03 → 1e3
10.00 → 10.0
0.0e+0 → 0e0
Radix
Canonicalization of
:radix[...]values remains representation-preserving rather than value-normalizing_separators are removed from the canonical payloadthe remaining digit sequence, decimal point placement, and leading zero width are otherwise preserved
// Non-canonical → Canonical
%10_00 → %1000
%10.00 → %10.00
%0010.00 → %0010.00
Booleans
Always true or false (lowercase).
Toggle
Preserve original literal (yes, on, etc.).
Base Literals
Hex: lowercase (
#ff00aa)Remove
_separators
Strings
Always double quotes (
")Minimal escaping
Raw line breaks →
\nNon-ASCII preserved as UTF-8
Canonical strings preserve their literal family:
ordinary strings always emit as escaped double-quoted strings, including when their decoded value contains line breaks
trimticks always retain the
>marker and backtick delimiterscanonical output never uses tabs in a multiline trimtick gutter
canonical equality is determined by the literal family and the resulting decoded or trimmed string value
one-line trimticks use the compact trimtick form, including inside inline containers
multiline trimticks rendered inside inline object or attribute forms remain compact trimticks with escaped line breaks rather than collapsing to ordinary strings
Separator Literals
Canonical separator literals:
No whitespace between
=and^No raw whitespace outside quoted segments
Raw segments are emitted verbatim
Quoted segments use canonical quoted-string escaping
// Non-canonical → Canonical
size:sep["x"] =^300x250 → size:sep["x"] = ^300x250
Quoted segments preserve their string content:
data:sep["|"] = ^"one "|" two" // preserved as-is
Symbolic Literals
Canonical symbolic literals use pipe delimiters and the decoded payload. A literal pipe is escaped as |, backslash and control characters use the ordinary canonical string escapes, and quote characters remain unescaped:
stage:symbol = |in review|
message:symbol = |say "yes" and \|wait\||
References
References MUST preserve semantic intent:
clone references remain clone references (
~...)pointer references remain pointer references (
~>...)
Canonical form MUST NOT:
Change a clone-intent reference into a pointer-intent reference or vice versa
Inline or resolve references
Alter the logical value graph
Canonical reference rendering also applies these normalizations:
explicit root prefixes are elided when redundant (
~$.a→~a,~>$.a→~>a)quoted member or attribute selectors may collapse to bare identifier form when the decoded segment is already a canonical bare identifier (
~a.@.["meta"]→~a.@.meta)
Node Heads
Canonical node-head ordering is:
tag@{...}:datatype
Canonical form preserves this order for node heads in the same way it preserves key@{...}:type ordering for bindings.
Whitespace
2-space indentation (no tabs)
One space around
=LF line endings (
\n)Opening brace on same line as binding
Closing brace on own line
user = {
name = "Patrik"
age = 49
}
Non-Goals
Canonical form does NOT:
Alter the logical value graph
Inline references
Change types
Add or remove bindings (except default header)