Skip to main content
Two clients that disagree on identity do not share a brain at all. So identity is the part of the protocol this SDK implements rather than delegates, and the part the paper leaves open that an SDK cannot.

Canonical serialization

The canonical form is JCS (RFC 8785), tagged in every envelope so it stays versionable:
Chosen over a binary encoding because a block is a small record and the protocol targets several languages: a canonical form a human can read and grep beats compactness here. Canonicalization erases the order a mapping was built in, which is what makes block_id a function of the content and nothing else:

Values a payload refuses

Floats and unsafe integers are refused inside a payload. A float fails at construction, not at commit.
JCS defines float serialization through ECMAScript rules that are hard to reproduce identically across languages, and integers outside the IEEE-754 safe range lose precision in any double-backed JSON parser. Either divergence would mean two conforming clients computing different block_id values for the same knowledge — which is the one failure the protocol cannot tolerate. If you need a number with a fraction, store it as a string or as a scaled integer and say which in the payload.

Three levels of hashes are three types

None is a str, and none is interchangeable with another.
Offering one level where another is expected is refused, not coerced:
That distinction matters in practice. Two clients that packed the same blocks with different gzip settings have different layer digests and the same Merkle root: the OciDigest says the bytes differ, the MerkleRoot says the knowledge does not.

Hashing primitives

The domain-separating prefixes are what stop a leaf hash from being replayed as an internal node — see Merkle DAGs.

Timestamps

One function, always UTC, so two clients recording the same event do not disagree about when it happened because of a local timezone.