The .axmr format¶
A ledger is a single file, extension .axmr, deliberately
inspectable with generic tools:
AXMR1
{"actor":…,"hash":…,"payload":…,"prev_hash":…,"seq":1,"sig":…,"signer":…,"ts":…,"type":…}
…
- Line 1 is the magic
AXMR1. Verification rejects anything else. - One event per line, canonical JSON — object keys sorted recursively, compact separators. Hashing is defined over this encoding, so independent implementations can verify each other's files byte-for-byte. The encoding is pinned in full below, and verification enforces it: a line that parses to the right event but is not spelled the canonical way fails verification.
- Appends are fsync'd.
- A future binary framing must preserve the same event model and hash definitions.
The canonical encoding, pinned¶
These rules are the whole contract — an implementation that follows them produces and verifies the same files as the reference:
- Objects: keys sorted lexicographically (byte order), recursively; compact separators — no whitespace anywhere.
- Strings: emitted as raw UTF-8. Only
"(as\"),\(as\\), and the control characters U+0000–U+001F are escaped — the short escapes\b \t \n \f \rwhere JSON defines them, otherwise\u00XXwith lowercase hex. Nothing else is escaped: non-ASCII characters (é, ☕, €) appear literally. - Numbers: integers are exact, in plain decimal. Non-integers
use the shortest decimal that round-trips to the same value, in
plain form (
0.30000000000000004,1.0,-0.0) or exponent form where the reference chooses it — the exponent always signed and never zero-padded (1e-7,5e-324,1e+21, never1e-07). Parsing must be correctly rounded (round-to-nearest-even), so a written decimal reads back as the identical value. - Event hash:
SHA-256(prev_hash ‖ canonical(body)), whereprev_hashenters as its 64-character lowercase hex text (not raw bytes) and the body is the canonical encoding of{seq, type, ts, actor, payload, prev_hash}. The genesis event'sprev_hashis 64 zeros. - Merkle root: the leaves are event-hash hex strings; a parent
is
SHA-256(left_hex ‖ right_hex)over the hex text; an odd element is paired with itself; a single element is its own root; the empty set's root isSHA-256(""). - Snapshot id:
SHA-256(canonical({"content": <content>, "parent": <parent-or-null>}))— the canonical encoding of that two-key object, hashed.
The event¶
Every event carries:
| Field | Meaning |
|---|---|
seq |
Position in the chain, from 1. |
prev_hash |
The previous event's hash (the genesis event points at the all-zero hash). |
hash |
SHA-256(prev_hash ‖ canonical(event body)). |
type |
The event kind (append, snapshot, dead, checkpoint, …). |
actor |
Who appended it. |
signer / sig |
The actor's Ed25519 public key and the signature over the event hash, for signed appends. |
ts |
Timestamp. |
payload |
The event body — for a snapshot, the record content. |
Records are lineages¶
- A record is a lineage of snapshots.
- A snapshot is content-addressed: its id is the hash of its content and its parent's id, under the pinned construction above; the first snapshot's parent is null.
- Revision = a new snapshot. The old one is superseded and stays readable.
- Withdrawal = a new snapshot with
status: "dead", a mandatoryreason, andscore: 0.0. Status serves correct consumers; 0.0 protects score-only consumers from continuing to use a dead record. - Score (epistemic weight) and status (lifecycle) are separate fields and are never conflated.
- The current view is a fold — the events applied in order to produce it: latest snapshot per record, dead excluded by default. It is a projection; producing it never alters the log.
Signing keys¶
Keys are Ed25519. generate_keypair() returns
(seed_hex, public_key_hex); seeds are held by actors and are never
written into the ledger. The ledger stores public keys (embedded per
event) and signatures only. Key rotation is an application-level act:
new appends use the new key, old signatures verify against the keys
embedded in their events — history is not re-signed, ever.
The erasure exception — specified, narrow, not implemented¶
Payloads are plaintext in v0.1. The design reserves exactly one exception to append-only, for documented legal compulsion: per-record payload encryption with an owner-signed erasure certificate appended to the chain and destruction of that record's key (the certificate, hashes, and signatures remain; the chain still verifies). It is not implemented in v0.1, has no API, and no deployment may imply it exists until it does. Ingest discipline is the primary defense: secrets and personal data should be referenced by hash, not embedded as payloads.
Non-goals (v0.1)¶
No query language, no secondary indexes, no networking/server mode, no multi-writer concurrency (a ledger has one sequencing writer; concurrent appends are serialized by the owning process), no compression. These are later layers built around the append-only core, never inside it.